Ramonda

Rules

ramonda-check runs 86 rules. Each has its own page: what it reports, why that is a fault, and what to write instead. The page and the terminal say the same thing, because both come from the rule.

Every one of them fails the run. There is no warning level: a warning that never fails anything is read once and then not read at all. Where a rule is wrong about your code, the answer is // ramonda-check-ignore <reason> on the line — which records the decision rather than hiding it, and is printed back on every run. See how to say "not here".

Looking for a rule by the trouble it explains rather than by its name? Something is wrong is indexed by what you can see, and Accessibility groups the thirty-five that are about it.

Every rule

  • props-written-by-the-receiver — a component or hook assigns to its own props — the write throws in every build, and the value belonged to whoever rendered the element
  • compute-takes-no-arguments — a @compute declares a parameter, and its cache is keyed by nothing so the argument is ignored
  • async-renderrender() is async, so it returns a promise where the diff expects markup
  • state-written-while-rendering — a state write is reached from render() or a @compute — directly, through a helper it calls, or three files away
  • state-mutated-in-place — a @state array or object is changed in place — this.items.push(…), this.user.name = … — so the signal never fires
  • decorator-that-adds-nothing — two decorators on one member give it the same thing — @persist beside @state, or one written twice
  • unkeyable-memoized-argument — a @memoized is called with — or declared to take — something a cache key cannot hold: a key holds a string, a number or a boolean
  • clock-read-while-renderingDate.now(), new Date() or Math.random() is reached from a render, by any path
  • ref-built-where-it-cannot-be-keptcreateRef() is called from a render, a @compute, a @memoized member a render calls, or a hook's props callback — so it answers a new identity every pass, the child re-renders for a ref that only looks changed, and nothing can read current
  • cached-read-of-a-plain-field — a @compute or a hook's props callback reads an ordinary field that is written after the first render, so the cached value goes stale
  • arrow-fields — a class field holds a function literal, so every instance builds a fresh one and props comparison can never match
  • browser-url — a component reads window.location in a project whose router already holds the answer
  • dom-writes — a component writes the document — document.body.classList.add(…) and its family — where render() could have said it
  • duplicate-decorators — a single-use decorator is written twice: @catchError, @ShouldUpdateOnPropsChange or @StableProps
  • unwatched-fields — a component reads a form field it does not watch, so it never re-renders when that field changes
  • watch-of-a-prop-that-is-not-there — a @watchProp selector names something the component's props type does not declare, so the method never runs
  • persist-of-a-lossy-value — a @persist field holds a Map, a Set, a Date, a function or a class instance, none of which JSON carries
  • unserializable-state — a @state field holds a Map, a Set, a Date, a function or a class instance, and the project renders on a server
  • interval-with-no-cleanup — a component starts a raw setInterval whose id nothing ever clears, so it keeps firing after unmount
  • listener-added-by-hand — a component adds a window or document listener by hand, where @onWindow or @onDocument would do it — or, inside if (__DEV__) where a decorator cannot be used, adds one that nothing ever removes
  • late-request-readrequestContext() is read below an await, after the request it names is gone
  • head-tags-collide — two tags in one Head resolve to the same identity, so only the second is written
  • unguarded-async-lifecycle — an async lifecycle awaits something with no try or .catch to handle a failure
  • context-consumed-above-its-provider — a component consumes a context on a line above the Provider that publishes it, so the consumer reads an ancestor's value
  • client-only-request-read — a requestContext() read is on a path that only runs in the browser, where the value it names is never available
  • one-provider-per-component — one component mounts two Providers of the same context, which core refuses at runtime
  • server-env-in-shared-codeprocess.env is read from a member the browser also runs, where process does not exist
  • fresh-object-in-hook-props — a hook — a context Provider above all — is handed an object or array built inside its props callback, where the callback also reads something reactive, so the value is rebuilt and every consumer of that key wakes with contents that did not change
  • fresh-value-from-a-watch-selector — a @watchProp selector builds the value it returns — an object or an array — so Object.is can never match it and the watcher fires on every props change with nothing changed
  • unsplittable-import — a dynamic import's path is neither a literal nor a template a bundler can read, so no chunk is emitted for it
  • unexposed-env-readimport.meta.env is read for a name @ramonda/build does not expose, so the value reads undefined
  • row-reads-a-plain-field — a list() row callback puts a field nothing can track into the markup, so a reused row keeps the old value
  • dev-guard-as-an-expression — a __DEV__ guard is written as && or ?: where an if would do the same thing
  • lens-path-through-a-gap — a focusOn write walks through a hop the types say may be null or undefined, which only the LAST hop creates
  • duplicate-key-among-siblings — two children written side by side claim the same literal key
  • row-without-a-key — a row built by map or by list() has no key
  • index-as-key — a row's key is built from the .map index and nothing else, which is the identity the diff already had
  • class-instead-of-classname — an element carries class where Ramonda reads className
  • tag-needs-its-parent — a tag is written outside the parent it requires — <tr> with no table above it, <option> with no select
  • parent-with-a-foreign-child — a container whose children are fixed by the content model holds a tag that is not one of them
  • interactive-inside-interactive — an interactive element is nested inside another of the same kind: a link in a link, a button in a button, a form in a form
  • unnamed-image — an img, area, image input or empty object has no alt, aria-label, aria-labelledby or title
  • unknown-aria-attribute — an aria-* attribute is not a name the ARIA specification has
  • unknown-role — a role names nothing, or names an abstract role that markup may not use
  • role-missing-required-aria — an explicit role is written without the aria-* its specification requires
  • role-takes-no-name — an aria-label or aria-labelledby is written on a role the specification forbids naming
  • region-with-no-namerole="region" is written with no aria-label, aria-labelledby or title, so it is not a landmark at all
  • false-on-a-boolean-attribute — a boolean attribute is written "false", which turns it ON because the parser reads only that it is there
  • misspelled-element-property — a name is written in the wrong case for element state that lives only in a property, so it is written as an attribute nothing reads
  • half-built-keyboard-path — an element with an interactive role and a pointer handler is missing the tabIndex or the key handler that would finish it
  • element-html-removed — a tag HTML has removed is written, so nothing defines what it means
  • option-that-cannot-chooseselected is written on an <option> inside a <Select>, which sets it from value instead
  • aria-value — an aria-* attribute carries a literal value its specification does not permit
  • aria-with-no-subject — a role or an aria-* sits on an element with no accessibility tree node to describe
  • empty-heading-or-link — a heading or a link has nothing inside it to announce
  • unnamed-frame — an iframe has no title
  • positive-tabindex — a tabIndex is above zero, which reorders the whole document rather than one element
  • aria-hidden-on-focusablearia-hidden="true" is written on an element a keyboard can still focus
  • aria-hidden-around-something-focusablearia-hidden="true" wraps something a keyboard can still tab to
  • presentation-role-on-focusablerole="presentation" is written on an element a keyboard can still focus, where the role is ignored
  • aria-state-with-no-role — an aria-* belonging to a role is written on an element that has no role
  • aria-state-the-role-does-not-have — an aria-* sits beside a role that does not support it, so nothing exposes it
  • aria-that-contradicts-the-tag — an aria-* is written false beside the HTML attribute that says the opposite
  • role-that-fights-the-tag — a role says the element behaves in a way the tag does not — a link as a button, or a button as a link
  • live-region-that-contradicts-its-role — an aria-live replaces the politeness the element's role already carries
  • autocomplete-that-fills-nothing — an autocomplete value names no autofill field, so the browser ignores it entirely
  • label-that-names-nothing — a <label> has no htmlFor and no control inside it, so it labels nothing
  • table-with-no-headers — a <table> written out with data rows has no <th> anywhere in it
  • link-without-a-destination — an <a> has no href, or one that goes nowhere — empty, #, or javascript:
  • fresh-object-in-props — a component is handed an object or array built during the render, so it is a new value every time and comparison can never match — lift it to a field or a @compute, or declare it on the child with @StableProps
  • function-built-in-the-markup — a function literal is written into a JSX attribute — in the attribute, on one side of a ternary or a ??, or in a local one line up — so its identity is fresh every render, and the listener is removed and re-added or the child can never compare its prop equal
  • object-among-the-children — a plain object is written among an element's children, where the runtime drops it and the page renders without it
  • function-used-as-a-tag — a plain function is written in tag position, where it names nothing the framework can construct — and the compiler only refuses the shapes that do not return exactly one element
  • click-with-no-keyboard-path — a click handler sits on a non-interactive element with no key handler, no tabIndex, no role and nothing interactive inside it
  • access-key — an accessKey is written, which overrides a shortcut the reader's own software may be using
  • attribute-that-does-nothing — one of six camelCase names — httpEquiv, acceptCharset, defaultValue, defaultChecked, innerHTML, textContent — reaches the DOM as itself, where no browser reads it
  • media-with-no-captions — a video or audio element carries no <track>, so nothing on the page says what is in it
  • duplicate-id — two elements in one render carry the same literal id, and both are always present
  • heading-skips-a-level — a heading is more than one level below the heading before it, both written in the same render
  • more-than-one-main — one render has more than one main landmark, where HTML allows one
  • landmarks-that-cannot-be-told-apart — one render has two or more landmarks of the same kind and none of them is named
  • lazy-imports-that-collide — two lazy functions are written identically but name different modules — the module cache is keyed by the function's source, so one entry has to serve both
  • fragment-link-to-nowhere — an href="#name" points at an id no element in the project carries
  • reference-to-an-id-that-is-not-there — an aria-labelledby, htmlFor or other id reference names an id no element in the project carries
  • control-with-no-label — a form control has no label, no aria-label, no aria-labelledby and no title, so nothing says what it is for
  • named-only-by-a-placeholder — a form control's only name is its placeholder, which disappears as soon as anybody types

Next

  • Checking your app — how to run it, and what it proves that a running page cannot.
  • Diagnostics — what the framework reports at runtime.