Repository navigation
v0.8.4
-
A failing report piped into another program arrives whole. The CLI
ended a run withprocess.exit( ), and a write to a pipe is asynchronous
on POSIX: whatever the pipe had not taken yet was dropped with the
process.abap2ui5lint src --json | jqon a failing corpus handed jq
exactly 65,536 bytes of a 175 KB document, and in a workflow the GitHub
annotations, printed last, were the first thing lost - on exactly the runs
whose report matters. A run now setsprocess.exitCodeand ends when its
output is out; the early exits (--explain,--help,--version,
--init) wait for stdout and stderr to drain first. Exit codes unchanged. -
abapGit's metadata XML is no longer read as a view. A file named on
the command line is collected when it starts with<, and every bulk way
of naming files names abapGit's sidecars too -abap2ui5lint src/*, a
pre-commit hook handing overgit diff --cached --name-only. Each
zcl_app.clas.xml/package.devc.xmlwas judged as a view whose root
control isabapGit: anaggregation-in-aggregationerror and exit 1 for
every class a commit touched. A document whose root element isabapGit
is not collected now, andcheckXmlSource( )(and so--stdin) returns
it with nothing to check;isAbapGitXml( )is exported from.. -
a( v = … n = … )reconstructs likea( n = … v = … ). ABAP passes
named parameters in any order, but the reconstructor read the value of
v,bandtas everything after its=up to the closing paren - so
with the name written second the value came out as
client->_bind_edit( mv_text ) n = \value``, was dropped as unresolvable
with a note, and the attribute (a binding, here) was missing from every
gate's view of the class. The arguments of an attribute call are read by
name now, with the paren- and literal-aware splitter the rules use. The
reconstructed documents of samples-controls, abap2UI5 and the fixtures (856
classes) are unchanged - none of them writes the other order. -
--fixno longer promises the next run work it will not find. An
edit inside text another fix of the same pass deleted was counted as
"deferred to the next run (overlapping)": a CRLF class with five dead
view_model_update( )lines reported 5 deferred - the\rof each
deleted line - and the next--fixhad nothing to do. Such an edit is moot
and no longer counted; one overlapping REPLACED text still is. -
The
--fixsummary counts problems, not edits. "fixed N problem(s)"
added up fix SPANS, and onecrlf-line-endingfinding carries a span per
line: a CRLF class with nine fixable findings said "would fix 45
problem(s)" under a dry-run list of nine. Both counts are findings now;
applyFixes( )returns the deferred ones asdeferredFindings
(additive). -
--watchno longer loses a save made during its first run. The
watchers were set up after the first run, which with the render gate is
seconds of browser launch and UI5 boot: a file saved in that window, after
the run had collected its files, was never seen, and the loop kept
reporting the old state until the next save. The watchers now exist
before the first run, and a change during it is one more run after it.
The watch/render test intest/review/watch.mjsraced the same window
and failed on a loaded machine. -
An output file that cannot be written is exit 2, and the report still
arrives whole.--sarif-out/--json-outinto a path that cannot be
created threw out of the run - a stack trace and exit 1, the findings
code, on a clean run - and so did--update-baseline; a badge that could
not be written exited throughprocess.exit(2)after the report had been
printed, which cut a piped report off at the pipe buffer again. Each is
one line on stderr and exit 2 now, and the files written beside the report
set the exit code instead of exiting. -
The render gate works under pnpm, and no longer hangs when it cannot.
The resource server served "the folder@openui5/sap.ui.coreis in", which
in a flat npm tree holds every library and under pnpm holds sap.ui.core
alone - so the bootstrap waited forsap.mforever and the run never
ended. Every@openui5package of the runtime is now resolved on its own,
and both waits are bounded (openRenderer({ bootTimeout, renderTimeout }),
90 s and 60 s): a UI5 that does not boot isERR_RENDER_RUNTIME_BROKEN
naming what the page logged, a document that does not finish rendering is
its own render error and its page is reloaded. -
The render runtime is also found in the project the run starts in.
npx --yes @abap2ui5/linter srcin a repository with
@abap2ui5/linter-renderas a devDependency said "12 of 12 packages
missing", and a global linter with a local runtime exited 2 under
--render: the runtime was looked for next to the linter only. It is now
looked for there first and inprocess.cwd( )second. -
A Chromium that will not start is a missing runtime, not a crash. With
@abap2ui5/linter-renderinstalled andnpx playwright install chromium
skipped, the run ended in Playwright'sbrowserType.launch: Executable doesn't existbanner, a stack trace and exit 1 - the code for findings.
Now it is one sentence (ERR_RENDER_BROWSER_MISSING, the fix named) and
exit 2 where the gate was asked for (--render,"render": true), and the
property gate alone with a notice where the gate was only left on - what a
missing runtime package already got.openRendereralso closes the HTTP
server it had started when the launch fails; it used to keep the process
alive. -
A render runtime of another UI5 release than the snapshot is named.
The property gate judges against the snapshot (1.152), the render gate
against the runtime;@abap2ui5/render-runtime0.1-0.6 serve 1.151, and a
1.152 member then failed view creation as "unknown setting" - a render
error that read like a broken view. The CLI now says so on stderr before
the run (runtimeSnapshotMismatch( )in./render). -
A baseline only speaks for the files a run linted. Every entry no
finding matched was STALE, whether or not its file was part of the run:
after--update-baselineoversrc(57 entries), linting one file with
--baselinereported "56 STALE entries" and exited 1 - also under
--advisory, whose help promises exit 0, and under--stdin. Now only an
entry of a file the run looked at can be stale - one it linted, or one
under a path it walked that it no longer collects (a class that stopped
building a view cannot have a finding, so its entries are stale like a
deleted file's) - or of a file that is gone from disk (nothing will ever
match it again).--update-baselineon one file replaced the whole
baseline with that file's entries; it now replaces the entries of the
files the run looked at and keeps the others, dropping those of deleted
files.--advisory/--fail-on neverreport stale entries and exit 0.
--jsoncarries an additivebaselineblock (file,suppressed,
byRule,stale: [{ key, count }]), so a document sayingfailing: 0
next to exit 1 names the reason.updateBaseline( )andkeyFile( )are
new in./baseline;applyBaseline( )andupdateBaseline( )take
{ scope }, the paths the run walked. -
--fixno longer deletes a namespace a helper still uses.
unused-namespace-declarationjudged the reconstructed view as if it were
the whole view. abap2UI5's ownz2ui5_cl_ui5_app_startdeclares
xmlns:formfor the SimpleForm itscreate_layout_form( )helper adds - a
RETURNING helper whose result is assigned, which the reconstructor does not
follow - and without its waiver comments the finding came with a deleting
fix that broke the view. The rule now stands down for a class whose
reconstruction is known incomplete (a builder call it could not place,
unplacedTokensonprepareAbap( )-helperTokenswithout a mere second
stringify( )of a finished view), and for a prefix the class writes in
more builder literals (ns = \form`,`form:X`,n = `core:require`) than its documents carry. A prefix written nowhere is still reported. A waiver of the rule where it stood down is unjudged, notunused-directive(applyDirectives( … { stoodDown })`): the rule did not judge that class,
and app_start's own waiver would otherwise have been reported as "remove
the directive" on every run over abap2UI5. -
New rule
malformed-xml(error). A raw*.view.xml/
*.fragment.xmlwith a duplicate attribute, a</contnt>for<content>
and no closing root tag passed--no-renderwith "Success!" and exit 0:
the XML reader behind the property gate is lenient on purpose, and nothing
asked whether the document was well-formed - which the browser's parser
decides before UI5 sees a control, refusing the whole view.parseXml( )
now records mismatched, unclosed and stray closing tags and duplicate
attributes asroot.malformed(tags inside CDATA and comments are text),
andcheckNodes( )reports each once, so the VS Code extension's and
mcp-server's self-assembled pipelines get it too. The tree the other rules
judge is built exactly as before. -
A CDATA section in a raw view is character data, to its
]]>. The XML
reader skipped comments but read a CDATA section as markup: a<Txt>in
one became an element (and anunknown-control), and a<!--in one -
script text testing for"<!--"- opened a comment that swallowed the
real tags behind the section up to the next-->, so a view xmllint
accepts was threemalformed-xmlerrors and the elements after the
section were never judged. Unchanged on the 1,390*.view.xml/
*.fragment.xmlfiles of the OpenUI5 samples and the corpora. -
unused-directiveno longer names a waiver for a rule that did not
run. Under--no-properties, or with the rule switched off or excluded
in therulesblock, a directive for it suppressed nothing because nothing
was judged - and the hint told the author to delete the waiver the full
run needs. Such an id is now unjudged rather than unused.applyDirectives( … { ran })takes the caller's answer (the entry points pass "not a
property-walk rule" when the property gate is off -WALK_ONLY_RULESin
./properties, gated against the emit sites), andruleRuns( )in
./findingsis therulesblock's. -
A bare directive is not called unused by a run without the property
gate either. Under--no-propertiesa directive with no id (so: every
rule) over a line whose only finding is a property-walk one still read
"suppressed nothing … remove the directive". It is unjudged whenever the
run left a gate out (parseDirectives( … { gatesRan })); a rule the
rulesblock switched off does not make it so, since the config speaks
for every run of the repository. -
ignoreand the render gate'sexcludemean the same thing however
the run is started. #35 made arules[id].excludepattern
match every spelling of a path - the absolute one a config'spaths
produce and the relative oneabap2ui5lint srcdoes.ignoreand
rules['render-error'].excludekept testing the path only as it was
reached, so abap2UI5's own"ignore": ["/src/99/"]dropped the frozen
package fromabap2ui5lintand not fromabap2ui5lint src, where its 17
classes came back with 86 findings and exit 1, and a render-error waiver
written the same way waived nothing. All three go through one matcher now
(pathMatches( )in./findings). -
--cacheno longer replays a render verdict of another runtime. The
cache key held the linter version, the snapshot and the settings, but not
the UI5 release@abap2ui5/linter-renderserves - which moves on its own,
and is exactly what the runtime-mismatch notice above tells a reader to
upgrade. The run afternpm i -D @abap2ui5/linter-render@latestreplayed
the old runtime's render errors. A rendering run's cache is now keyed by
that release too; a property-only run's is not, and survives the upgrade. -
--format markdownkeeps the tags its messages quote. A table cell
passed a message through as raw HTML, and GitHub's sanitizer drops an
element it does not know: in a PR comment or a job summary
<Page> carries the attribute title twiceread " carries the attribute
title twice",close <content> with </content>read "close with ", and
<mvc:View>became a link tomvc:View.&,<and>are escaped as
entities now. -
--format sarifwrites each location as a URI. abapGit names a
namespaced object's file with#for its namespace slashes
(#abc#cl_app.clas.abap), and SARIF'sartifactLocation.uriis a URI
reference, where#starts the fragment: the location namedsrc/, and
code scanning had no file to put the alert on. A blank in a path was no
URI character either. Every segment is percent-encoded now. -
A
--no-renderrun over a corpus is about a quarter faster. Every
reader of DATA/TYPES/CONSTANTS declarations split the whole class into
statements again - threeparseData( )and fourstaticAttributes( )
splits per class, over the same scrubbed source - which was 121 MB of
splitting for the 12 MB of samples-controls' 642 classes and a quarter of
the run.splitStatements( )is memoized likescrub( )(its shared
result frozen), and the run went from ~13 s to ~10 s with byte-identical
findings over samples-controls and abap2UI5. -
A byte-order mark no longer breaks the config or the baseline. A file
saved by Notepad or PowerShell'sOut-Filestarted with U+FEFF and failed
as "Unexpected token"; it is stripped before parsing - also from the
preview data--screenshot-modelnames and from the<class>.mock.json
the screenshot reads by convention. -
--initwrites a$schemathe new file can reach. It was always
./node_modules/@abap2ui5/linter/…, wrong in a monorepo package and
pointing at nothing undernpxor a global install. It is now the relative
path to the nearestnode_modules/@abap2ui5/linterthat is the running
linter, and otherwise the published schema of exactly this version
(https://unpkg.com/@abap2ui5/linter@<version>/…) - also where npx's own
copy sits below the new file (--initin the home directory, or with
npm's cache inside the project): a path into a cache is none to commit. -
--initno longer shadows anabap2ui5lint.json. Discovery reads
abap2ui5lint.jsoncbeforeabap2ui5lint.json, and--initonly checked
for the first: beside an existing.jsonit wrote a fresh.jsonc, which
from then on silently won (a repository with"failOn": "never"went from
exit 0 to exit 1). It refuses beside either spelling now. -
A missing
extendstarget says so. It was reported as "no such file -
check the --config path", also when no--configwas given; the message
now names theextendsvalue and where it was looked for, and a directory
is named as one. -
scripts/generate-metadata.mjsfinds the sources where they are
installed. The shipped generator looked in
<linter>/node_modules/@openui5only, which a consumer's tree never has (npm
hoists above it, pnpm keeps them beside@abap2ui5/linter-render). It now
also resolves each package through@abap2ui5/linter-render, through its
own package and through the current directory. -
The
@abap2ui5/linter-renderpeer range starts at its first
published line.>=0.7.0 <0.9.0named a 0.7.0 the registry never had -
the name starts at 0.8.0; 0.7.0 is the last@abap2ui5/render-runtime.
FLOORinscripts/peer-range.mjsis 0.8.0 and the range
>=0.8.0 <0.9.0. The legacy@abap2ui5/render-runtimerange stays
>=0.1.0 <0.8.0on purpose: its 0.1-0.6 lines serve UI5 1.151 against the
1.152 snapshot, but narrowing an optional peer is an ERESOLVE for every
project still on them, so the version gap is named on stderr instead (see
above). -
The docs no longer send
npxto an unregistered name. The issue
templates asked fornpx abap2ui5lint --version;abap2ui5lintis the
command the package installs, not a package on npm, so outside a project
that installed it npx would download whatever someone publishes under that
name. Where an install is implied (the README's "stay green" block, the
release smoke test, the rules page, the templates) it is now
npx --no-install abap2ui5lint, and where none is, the scoped
npx @abap2ui5/linter. AGENTS.md now records samples-controls' npm
dependency as it is (^0.8.3, no longer a^0.5.1git dependency).