Diagnostics
The compiler reports every problem it finds as a diagnostic, with a stable code such as UF3013. A code is never reused or renumbered. The compiler never copies code it has not analysed into an output, so anything it cannot lower to every target is a diagnostic, never a silent difference.
A diagnostic has a severity, a message that names what was found, the place in the source, help, and, where the rewrite is mechanical, fixes. Only an error stops a component from compiling. A fix is safe when it can be applied without a review, and likely when it needs one. The compiler links each code to its section on this page.
This file has no key on its list item:
// UF3013 missing-key: a list item without `key`; the likely fix keys it by its index.
export interface TagListProps {
tags: string[];
}
export default function TagList({ tags }: TagListProps) {
return (
<ul>
{tags.map((tag) => (
<li>{tag}</li>
))}
</ul>
);
}
The compiler reports it, with a likely fix:
[
{
"code": "UF3013",
"severity": "error",
"message": "A list's element needs a `key`: the frameworks match each item's element across renders by it.",
"file": "diagnostics/missing-key/TagList.uf.tsx",
"start": {
"line": 10,
"column": 10,
"offset": 254
},
"end": {
"line": 10,
"column": 12,
"offset": 256
},
"help": "Key it by something unique and stable in the item: `key={item.id}`.",
"fixes": [
{
"title": "Key the element by its index, `key={index}`",
"confidence": "likely",
"edits": [
{
"span": {
"start": 238,
"end": 238
},
"text": ", index"
},
{
"span": {
"start": 256,
"end": 256
},
"text": " key={index}"
}
]
}
]
}
]
Codes are grouped in bands:
| Band | Covers | |
|---|---|---|
| UF1xxx | Syntax and components | |
| UF2xxx | Setup and macros | |
| UF3xxx | JSX and cross-component usage | |
| UF4xxx | Portability (capabilities) | |
| UF5xxx | Styles | No codes yet |
| UF6xxx | Types | No codes yet |
| UF7xxx | Config and tooling | No codes yet |
| UF8xxx | Plugins | |
| UF9xxx | Internal |
Syntax and components
UF1001 syntax-error
Error. The file is not valid TSX.
The parser could not read the file as TSX. The message names the first problem it met.
Fix the syntax error. Any TypeScript editor shows the same error, because a .uf.tsx file
is ordinary TSX.
A raw } or > in JSX text is a syntax error: write } or >, which every JSX
implementation decodes (none decodes }), or a string expression ({"}"}). The
fix writes the reference where the file then parses. Where it would not, the } is more
likely code after an element that is not closed.
UF1002 unsupported-syntax
Error. The compiler cannot lower this construct yet.
The construct is part of the source language, but this version of the compiler cannot lower it to every target yet. The compiler never copies code it has not analysed into an output, so it reports the construct instead.
Rewrite the code without the construct, or wait for the release that supports it. The
message names the construct, and the milestone that lowers it where one does. Some
constructs are outside the subset because one target cannot read them: Angular's template
language has no block-bodied arrow functions, type assertions, hexadecimal numbers or
\u{…} escapes, so expressions keep to what every target reads alike. Where a mechanical
rewrite exists (0x10 to 16, { return e; } to e), the fix applies it.
Some attributes wait for a target: a name a target's element types do not declare fails
that target's type-check whatever its value (writingsuggestions, commandfor and
dirname in React's; popover and closedby in Vue's, which are also the authoring
types; ARIA 1.3's aria-description in Vue's and Svelte's), and a bound
size or multiple on a <select>, or disabled on an <option> or <optgroup>,
decides which option starts selected, which some clients settle before they bind it (form
state, M3).
UF1101 no-component
Error. The file exports no component.
A .uf.tsx file holds components: exported PascalCase functions whose last statement
returns JSX.
export default function Greeting() {
return <p>Hello, world!</p>;
}
Export a component, or move framework-agnostic code to a plain .ts module.
UF1102 invalid-component
Error. A component must be a named PascalCase function that returns JSX.
A component is a function whose body is its setup, and whose last statement returns the JSX that is its template:
export default function Greeting() {
return <p>Hello, world!</p>;
}
The compiler reports a component without a name (export default function () { … }), a
generator, one whose last statement does not return JSX, and one that renders nothing
(return <></>;).
A component returns an element or a fragment. A conditional or a list (return c ? <A /> : <B />;) belongs inside one: the fix wraps it in <>…</>, which renders the same.
A component is a function declaration. One written as a value, an arrow function or a
function expression in a const (export const Card = (props: CardProps) => …), is
reported, and still checked as the declaration it would be: the fix declares it as
export function Card(props: CardProps) { return …; }, keeping its parameters and body.
It also reports an exported function that returns JSX, as a component does, under a name
that is not PascalCase in ASCII letters and digits ([A-Z][A-Za-z0-9]*). JSX reads a tag
that starts with a lower-case letter (<card>) as an HTML element, and every target names
files after the component (Card.vue, and card.ts on Angular, whose selector is
uf-card), so its name keeps to the characters every file system and target reads alike.
The fix renames the function where its name is used nowhere else.
UF1103 invalid-export-name
Error. A component is exported under a name that is not an identifier.
Every target writes a component's exports as code, and consumers import a component by name to use it as a JSX tag, so its exported name must be an identifier.
export { Card as "my card" }; // rejected
export { Card as MyCard }; // accepted
UF1104 component-name-collision
Error. Two components' names differ only in case.
Each component compiles to a file named after it: Card.vue, and card.ts on Angular.
Two names that differ only in case give the same Angular file and selector, and files
that overwrite each other on case-insensitive file systems (macOS and Windows).
Rename one of the components.
UF1201 framework-import
Error. Components are framework-free.
A .uf.tsx component is compiled to every framework, so it cannot import one. The
compiler writes each framework's code for you: hooks in React, refs in Vue, runes in
Svelte, signals in Solid.
Write the component with the authoring API from unframework instead:
import { ref } from "unframework";
export default function Counter() {
const count = ref(0);
// …
}
Setup and macros
UF2001 invalid-props
Error. The props parameter is not in a form every target can declare.
A component takes its props as its only parameter, typed by an object type: either
destructured, with optional static defaults, or as one object read as props.x.
export default function Badge({ label, tone = "info" }: BadgeProps) { … }
export function Plain(props: PlainProps) { return <p>{props.label}</p>; }
The compiler reports a parameter without a type annotation, a second parameter, a
renamed, nested or computed property in the pattern, a member a props type declares
twice, which TypeScript rejects too, a default on a required prop, a default on a prop
whose type admits null (unless the default is null: Qwik applies a destructured
default to null too), and, in the object form, a use of the object other than reading
one of its members (props, props[key], props.x = …, (props).x) and a name
starting with $$: Astro's compiled component declares $$props, $$result, $$slots
and $$render where its output declares the object.
Destructure the props by their own names, declare each once, make a prop with a default optional, and read the object form only through its members.
UF2002 non-static-prop-default
Error. A prop's default must be a static value.
Vue hoists defineProps defaults out of the component and Angular reads an input's
default before any input is set, so a default cannot read anything: not a prop, not a
global, not a function's result.
A default is a string, number, boolean or null literal, a negated number, a template
literal without expressions, or an array or object literal of those (identifier or string
keys, no spreads).
({ page = 1, tags = ["new"] }: P) // accepted
({ pageCount = Number.MAX_SAFE_INTEGER }: P) // rejected: reads a global
Write the value itself, or compute it in the template (pageCount ?? 10).
UF2003 reserved-prop-name
Error. A target reserves this prop name.
Every target declares each prop by its name and reads it in its templates, so a prop's name keeps to ASCII letters and digits (Angular's expression lexer reads no other letter), starting with a letter, and none a target gives a meaning of its own:
key,ref,children,class,style,slot,is,ref_forandref_key, which React, Vue, Svelte or Astro read themselves and never pass to the component;props,rawProps,constructorandAstro, which the outputs declare beside the props, andFragment, which Astro's output imports to render<>;- JavaScript's reserved words, Angular's expression keywords (
as,let,typeof, …) and the globals expressions may read (Math,String, …); - names of events (
onClick: events land in M2), names starting withngand a capital letter (ngIf), which Angular reserves for its directives, and names ending in$, which Qwik reads as a QRL.
The outputs also declare types named Props and CSSProperties, Angular's output imports
its Component decorator, which is also a type, and reads TypeScript's Exclude, and
Solid's output reads TypeScript's Partial, Record, Required and Pick, so a local
type of any of these names is reported. Props is accepted as a component's own props
type, when every component whose props reach it takes it as its props type: Astro's
output declares a Props of its own for any other.
Rename the prop or the type.
JSX and cross-component usage
UF3001 unknown-element
Error. The element is not an HTML element.
Lower-case JSX tags are HTML elements, and the compiler emits them unchanged into every
target. A tag that HTML does not define, an obsolete one (<center>, <marquee>) or one
in the wrong case (<dIV>) would be read differently by each framework: Vue resolves it
as a component, Angular rejects it, and the browser renders an unknown element.
Inside an <svg>, tags are SVG elements in SVG's own case (linearGradient,
clipPath): an HTML element there, or an SVG element outside an <svg>, is reported too.
Use the HTML or SVG element; a misspelt case has a fix. Components are PascalCase
(<Card>).
UF3002 reserved-element
Error. A component cannot render this element.
Some elements belong to the document or to a framework's template syntax, not to a component's markup:
<html>,<head>and<body>are the document's; the browser drops them inside one.<title>,<meta>,<link>and<base>describe the document: React moves them into the<head>, and the other frameworks leave them in place.<script>and<style>hold code the compiler cannot analyse; Vue and Angular drop them. A component's styles are a stylesheet it imports. This holds inside an<svg>too.<template>,<slot>,<component>,<transition>and Angular's<ng-…>elements are template syntax in one framework or another.- SVG's animation elements (
<animate>,<animateMotion>,<animateTransform>,<set>and<discard>) make the rendering depend on time, and<animate>and<set>can set any attribute, ajavascript:hrefincluded.
UF3003 invalid-nesting
Error. The browser or a framework would repair this markup.
The HTML parser repairs some nesting: a <div> closes an open <p>, a <tr> directly in
a <table> gets a <tbody>, text in a table moves out of it, and a void element such as
<br> cannot hold children. Server-rendered HTML then becomes a different tree from the
one a client renderer builds, and Svelte, Vue and React each report it. Svelte's compiler
also drops whitespace on its own inside a <select> or a <datalist>.
The rules see through conditionals, lists and fragments: rows that a .map renders
directly in a <table> get a <tbody> too. An expression renders text, so it cannot sit
where text cannot (a table part, a <select>), and inside an <svg> only <text>,
<tspan>, <textPath>, <title> and <desc> render text. Svelte drops whitespace-only
text in SVG outside a <text>, so it is reported there too. SVG's <title> and <desc>
hold text only: the HTML parser and Vue read an element inside them as HTML, and React
as SVG.
The parser reads an <iframe>'s content as raw text, decoding no character reference and
reading no comment, so text other than whitespace, an expression or a conditional there
renders the servers' escapes and markers as text, which a client render does not. A
browser never shows that content: remove it.
Write the markup the parser keeps: wrap rows in <tbody>, use a <div> where a
paragraph would contain blocks, and put options on lines of their own.
UF3004 non-canonical-attribute
Error. The attribute is not written in its canonical form.
Each attribute has one way to be written (P3):
- HTML names, in lower case:
class,for,tabindex, notclassName,htmlForortabIndex. - A boolean attribute without a value:
disabled, notdisabled=""ordisabled="disabled". Its presence turns it on:disabled="false"is still disabled. - A value for every other attribute: in JSX a bare attribute means the string
"true", which is only meaningful for ARIA states,data-*and true/false attributes. - Numbers in canonical form (
3,0.5): some renderers set these attributes as DOM properties, which rewrites any other spelling. - No
classrather than an empty one: Vue rendersclass="", the others leave it out. (Whitespace between class names needs no fix: the compiler writes one space.) - A literal as a literal:
title="x", nottitle={"x"};disabled, notdisabled={true}; no attribute rather than{false},{null}or{undefined};class="a b", notclass={["a", "b"]}; the attributes themselves, not a spread of an object literal. - Style keys in camel case (
marginTop), as React's and Vue's style objects write them. href, not SVG's oldxlink:href.key, in lower case, on a list's element: in JSXKEYis an attribute.- No spread that renders nothing: of a type that declares no keys, or of a source that a
condition around the spread shows is absent (
{attrs ? null : <p {...attrs} />}). - An enumerated attribute's keyword in lower case (
dir="rtl", notdir="RTL"), and the keyword HTML reads an empty value as (spellcheck="true",popover="auto", a<button>'stype="submit"): the targets' types take only those spellings.
The fix rewrites the attribute where the rewrite is mechanical.
UF3005 reserved-attribute
Error. The attribute belongs to a framework.
The attribute is syntax or data of one of the target frameworks, so that framework would not render it as an attribute:
- Angular binds
bind-*,on-*,bindon-*,ref-*andlet-*, consumesi18nand reservesng*names. slotandisare template syntax in Svelte, Astro and Vue.children,dangerouslySetInnerHTML,textContentand React'ssuppress…props are framework props, not attributes; write the content as JSX children.data-hk,data-astro-*,data-v-*,data-qwik-*anddata-uf-*are attributes that frameworks or the compiler add themselves, and ids starting withuf-id-are the ones the compiler generates.xmlns,xmlns:*,xml:*andxlink:*(butxlink:href, which ishref) are XML's: every target writes SVG's namespace itself, and the HTML parser and the clients disagree about namespaced attributes.
UF3006 unknown-attribute
Error. The attribute is not an attribute of this element.
Elements take HTML's attributes: the global ones (id, class, title, hidden, …),
their own (href on <a>, alt on <img>), ARIA's role and aria-*, and data-*.
SVG elements take SVG's attributes, in SVG's own case (viewBox, stroke-width).
Frameworks treat other names inconsistently: Vue sets a name that matches a DOM property
as that property, and React and Solid give some names meaning of their own.
Use the element's attribute, or a data-* attribute for your own data.
UF3007 duplicate-attribute
Error. The element has the same attribute twice.
Attribute names are case-insensitive in HTML, and className is class. With the same
attribute twice, React keeps the last value, the HTML parser the first, and Vue and Svelte
fail to compile. Keep one.
A spread sets every key its type declares, so a key that is also written on the element,
or set by another spread, is set twice too. class is the exception: one spread's class
merges with the element's own. A class name listed twice in one class is reported as
well: Angular merges a static class with a bound one through the class list, which drops
the repeat the other targets keep. The fix lists a static class's names once. So is a
second key on a list's element: the list has one key.
UF3008 unportable-attribute-value
Error. The targets would render this attribute value differently.
Some values cannot be rendered the same way on every target, and some hold code or a document the compiler cannot analyse, which it never copies into an output:
javascript:URLs run code the compiler cannot analyse, and React blocks them. Event handlers are the way to run code.srcdocholds an HTML document, scripts included, which the frame runs as the page's own origin unless it is sandboxed. Adata:URL that a frame loads (an<iframe>'s or an<embed>'ssrc, an<object>'sdata) is a document too. Load the document from a URL, or write its markup in the component.data:images and media, andblob:URLs, hold no code the page runs, and are accepted.- An empty
src, or an emptyhrefoutside<a>: React drops it and warns. hidden="until-found": React rendershiddenas a boolean.- A number out of range, or not a number, in a numeric attribute (
rows,span,<li value>): renderers that set the DOM property rewrite or reject it. - A class name holding whitespace other than ASCII's (a no-break space): Angular splits class names there, and the other targets keep it in the name.
- An attribute that React and Qwik type as a number (
tabindex,colspan,aria-level) with a value that is not one: they write it as a number literal. - An ARIA value ARIA does not define (
aria-hidden="yes",aria-live="loud"), or arolethat is not one of ARIA's roles: assistive technology ignores it, and Svelte warns about it. - A value of an enumerated attribute that some target's types do not list (a
<bdo>'sdir="auto",role="generic",autocomplete="bday", a<button>'stype="menu"): that target's output fails its type-check. The message lists the values every target takes.
UF3009 ambiguous-jsx-text
Error. JSX implementations read this text differently.
JSX does not define its text exactly, and its implementations disagree. Babel (which
Vue's and Solid's JSX use) decodes character references first and then trims the lines,
trimming only spaces and tabs. TypeScript, oxc and esbuild trim the raw lines first, also
trimming Unicode whitespace such as a no-break space, and read U+2028 and U+2029 as line
breaks. So   at the end of a line, , a tab, or a raw no-break space next to a
line break means one thing to some tools and another to the rest.
The compiler lowers text only where every implementation agrees. Write such characters
as references ( ), write a space for a tab, and keep a significant space on the
same line as the text it separates. Numeric references the implementations decode
differently (&#+65;, a surrogate such as �, a number past U+10FFFF) are
reported too, in attribute strings as well.
UF3010 unportable-character
Error. HTML would not keep this character.
Each target renders the component's markup on the server as HTML, and the browser parses it, so a static value must hold only characters the HTML parser keeps as they are:
- a carriage return becomes a line feed (Svelte's compiler converts it too); in a file with CRLF line endings, a value written across lines holds one;
- NUL (U+0000) is dropped from text and replaced in attribute values;
- a lone UTF-16 surrogate cannot be encoded in UTF-8;
- the control characters other than whitespace, and the noncharacters, are not allowed in an HTML document.
Remove the character. A carriage return from a line ending has a fix.
UF3011 html-only-reference
Warning. JSX does not decode this HTML character reference.
JSX decodes only the named character references of XHTML 1.0 (&, ,
©), and numeric ones (✓). The names HTML added later (✓,
}, 
) stay as written: every target renders ✓ as those seven
characters, where an HTML or Vue template would show ✓.
Write the numeric reference for the character (the fix, where it changes nothing else),
or &check; for the text itself.
UF3012 jsx-outside-template
Error. JSX can only be in a component's template.
A component's template is the JSX its last statement returns, with the slot functions in
it. The compiler lowers that tree to each target's template, and Vue's, Svelte's and
Angular's templates are not JSX: they have no counterpart for JSX kept in a variable,
returned from a helper, passed as a prop that is not a slot, or used as a value (an
attribute's value, a function's argument, an array's element). JSX can be a child, a
branch of a conditional child (c ? <A /> : <B />, c && <A />) or the element a .map
child renders.
function renderIcon() {
return <i class="icon" />; // rejected: a helper that returns JSX
}
Inline the JSX where it is used, move it into a slot function, or extract a component.
UF3013 missing-key
Error. A list's element needs a key.
A .map child renders one element per item, and the frameworks match the elements of
two renders by their key. Without one, React warns and the targets disagree about
which element an item keeps.
{items.map((item) => <li key={item.id}>{item.name}</li>)}
Key the element by something unique and stable in the item. The fix keys it by its index, which is unique but moves with the item: review it.
UF3014 misplaced-key
Error. A key belongs on the element a list renders.
key is not an attribute: it tells the frameworks which item of a list an element
renders, and only the element a .map callback returns has one. Elsewhere React and Vue
use it to remount an element and the others ignore it. A constant key on a list's
element gives every item the same key.
The fix removes a misplaced key, and keys a list's element by its index.
UF3015 invalid-list
Error. A list is written source.map((item, index) => <element key={…}>…</element>).
A .map child lowers to each target's list: Vue's v-for, Svelte's {#each}, Angular's
@for. Its callback is an arrow function with one or two plain parameters, the item and
its index, that returns one element:
{users.map((user, index) => (
<li key={user.id}>{index + 1}. {user.name}</li>
))}
Read an item's members as item.name rather than destructuring it (an entry of
Object.entries as entry[0] and entry[1]), and wrap several elements in one. An item
that renders or not is filtered out before mapping (users.filter((user) => user.active).map(…)); one that renders one element or another keeps one keyed element
that holds the conditional (<li key={link.id}>{link.href ? <a …>…</a> : <span>…</span>}</li>).
UF3016 non-text-interpolation
Error. An expression child must render text.
An expression child renders its value as text. JSX renders true, false and arrays
its own way, and the template targets render them as text ({{ true }} is "true" on
Vue), so an expression child must be a string or a number, or null or undefined, which
render nothing.
Turn the value into text: done ? "Done" : "Open" for a boolean, .join(", ") or
.map(…) for an array, a member for an object. A literal {true} or {false} child
renders nothing: remove it.
UF3017 dropped-line-feed
Error. The HTML parser drops a line feed at the start of this element.
The HTML parser drops a line feed that starts a <pre>, a <textarea> or a <listing>,
and React's server renderer writes one more to keep it. Server-rendered HTML and a client
render then disagree about the first line.
A text after a conditional or a list that can render nothing starts the element when it does: React's and Astro's servers then write nothing before it, so the parser drops its line feed, and the other targets write a comment, which keeps it.
Start the element's text with something other than a line feed.
UF3018 unportable-binding
Error. The targets render or type this bound value differently.
A bound value renders by the rules of where it is bound, and the targets agree only for some kinds of value:
- a boolean attribute (
disabled) takes a boolean; ARIA's true/false states (aria-hidden),draggableandspellcheckalso take booleans, as"true"and"false"; - an attribute some target's types restrict to a list of tokens (
autocomplete,inputmode,role,aria-live, SVG'sfill-rule) takes a string literal, or a union of them, that every target's types accept: the diagnostic lists them; data-*and other attributes take strings and numbers: Qwik dropsfalsethere, React drops it on some, and the others write"false"(writeString(value));- an attribute React and Qwik type as a number (
tabindex,colspan) takes a number, and one some target's types declare as a string (title,id,aria-label, SVG'sd) takes a string; - a class name takes a string, a style value a string, or a number where CSS needs no
unit (
opacity,line-height): React and Qwik addpxto other numbers but0, and Qwik also to SVG's stroke and opacity properties (stroke-width), which React leaves bare. A literal0is the value0on any property; - a list's source is an array, and its key a string or a number that reads the item or
its index: a key that reads neither is the same for every item, which Vue and Svelte
reject. It identifies the item within its own list, so it reads no variable of a list
around it: Angular's
trackreads only its own item,$indexand the component's members. A source that can be absent is defaulted,(items ?? []).map(…), which the fix writes foritems?.map(…).
null and undefined leave the attribute or the declaration out everywhere, but for a
value some targets set as a property (an <li>'s, a <meter>'s, an <option>'s:
UF1002). Convert the value, or bind it where its kind renders alike.
UF3019 nondeterministic-render
Error. Rendering must not depend on time, chance or the machine.
A component renders on the server and again in the browser, and both renders must give
the same markup (plan §4.5): rendering is a function of props, state and slots.
Date, Intl, crypto, performance, globalThis and Math.random read the clock,
chance or the machine, and toLocaleString, toLocaleDateString, toLocaleTimeString,
toLocaleUpperCase, toLocaleLowerCase and localeCompare read the machine's locale.
Pass the value in as a prop, computed where it is decided.
UF3020 unresolved-reference
Error. The name is no prop, list item or global an expression can read.
An expression in the template reads the component's props, the item and index of the
lists around it, the parameters of the arrow functions in it, and a few pure globals:
undefined, NaN, Infinity, Math, Number, String, Boolean, Array, Object,
JSON, parseInt, parseFloat, isNaN, isFinite and the URI functions. The targets
can reach nothing else: Angular's templates see only the component's members, and the
outputs run where window may not exist.
Declare the value as a prop, or check the name's spelling.
UF3021 impure-render-expression
Error. A template expression must not change anything.
Rendering is a pure function of props, state and slots (plan §4.5): a framework renders
when it decides to, sometimes twice, and Svelte throws when a template expression changes
state. So a template expression cannot assign, update or delete, use this, await,
yield, void or the comma operator, define a function or a class, or call a method that
changes its receiver: sort, reverse, splice, push, pop, shift, unshift,
fill, copyWithin, and Object.assign and its kin.
Use the methods that return a new array: the fix writes toSorted, toReversed and
toSpliced. An arrow function is a call's argument (items.filter((item) => item.on)).
UF3022 invalid-class-or-style
Error. The class or style is malformed.
A class and a style are lowered part by part, and every target writes the parts its
own way, so each part must be one the targets read alike:
- class and style objects have plain keys: no computed keys, spreads or methods;
- a style string parses as CSS declarations, and a style sets something;
- each property is one the browsers know (or a custom property,
--gap): an unknown name is almost always a typo, which no browser applies and Solid's lint rejects; - a custom property's name is in lower case: Angular's compiler and its server renderer
lowercase it, so
var(--myGap)would not find--myGapthere; - a style sets each property once, and never a shorthand beside one of its longhands
(
marginandmarginTop), or a flow-relative property beside a physical one it can be (marginInlineStartandmarginLeft): React's, Solid's and Qwik's style objects cannot keep the order that decides the result; - no
!important, which React's style objects drop; - a static value reads the same to Angular's style parser, which knows no escapes or
comments: a string holds no escaped quote of its own kind (put the other quote around
it), the parentheses inside strings balance (write
\28for a lone one), and a comment holds no quote, parenthesis or;.
Write each property once, with its final value.
UF3023 non-nullable-operand
Error. The operand of ?? or ?. is never null or undefined.
a ?? b and a?.b act only when a is null or undefined. When a can be neither
where it is read, the operator does nothing, and Angular's compiler rejects it (NG8102,
NG8107). a can be neither when its type excludes both, or when a condition around it
rules them out as TypeScript narrows it: a test of a itself, through !, && and ||,
a comparison with null, undefined or a value, typeof, a member read through ?.,
Array.isArray, or a discriminant of the union a belongs to:
{box.inner && <p title={box.inner?.title}>…</p>} // reported: write `box.inner.title`
Remove the operator, which the fix does, or make the prop optional if it can be absent.
Where the targets' checkers may read a apart, the compiler reports, as not supported
yet (UF1002), a use that relies on a narrowing some of them do not see: a member, or a
prop of the object form, a condition narrows outside a list's callback (Angular's loops
keep the narrowing, the JSX targets' callbacks forget it); a prop an expression's own
conditional narrows, used in an arrow function in it (Solid copies it with props.x,
which the closure forgets); and a prop a list's key uses (Angular's track reads the
input again). Testing the value, reading it through ?., giving it a value with ??,
comparing it for equality and writing it into a string read alike everywhere. Where the
compiler does not follow a test (an equality with a value that is no literal), Angular
may narrow a and reject the operator, which is reported too. A destructured prop a
conditional child narrows stays narrowed in its branch's lists and arrow functions on
every target. Reading a member of a value a condition shows absent is reported as well.
UF3024 shadowed-binding
Error. A parameter in the template shadows a name the outputs read, or is never read.
The targets rewrite the names a template expression reads: Solid reads a prop label as
props.label, and Angular reads the template variable it declares for it,
@let label = this.label();. A .map or arrow function parameter that has the name of a
prop, of the object form's props parameter, of the item or index of a list around it, of
a global an expression may read (String), the name props or rawProps, a name
starting with $ (Angular's @for declares $index, $count and others), Fragment
(Astro's output imports it to render <>) or a name starting with _ but _ itself
(Vue's compiled render functions declare _ctx, _cache, __props and helpers such as
_toDisplayString) would capture those rewrites or those names. A parameter named after
one of Angular's expression keywords (as) cannot be read in its templates at all.
Rename the parameter.
An arrow function's parameter that nothing after it reads fails the outputs' lint
(no-unused-vars). The fix removes it. (A list's unread index needs no fix: the targets
leave it out.)
UF3025 unsupported-conditional
Error. A conditional child is written c ? <A /> : <B /> or c && <A />.
x || <B /> and x ?? <B /> render either the value of x or an element, which no
template target can express as one conditional. Write the conditional with ?:: x ? x : <B /> for ||, and x != null ? x : <B /> for ??, which renders 0 and "" as ??
does.
{message ? message : <p>All good.</p>}
{count != null ? count : <p>None yet.</p>}
When x is a prop or a member of one that renders as text, the fix writes the
conditional.
Portability (capabilities)
UF4001 unsupported-capability
Warning. A selected target cannot support a feature the component uses.
Each target declares, for every feature, whether it supports it natively, emulates it with an inline helper, or cannot support it. The component uses a feature that a selected target cannot support; the diagnostic points at its first use, and the message names the feature and the target's reason. For example, Astro renders on the server only, so event handlers are inert there.
A feature can also be a construct one framework renders differently from HTML and the
other targets. Vue's client, for example, selects the first option of a single-selection
list box (<select size="2"> without multiple), where HTML selects none, so Vue
declares listbox unsupported, and the diagnostic points at the list box's size.
The severity comes from the target's capability matrix. With strict: true, portability
warnings are errors. Remove the feature, or drop the target.
Plugins
UF8001 plugin-failed
Error. A compiler plugin failed.
A compiler plugin's ir or output hook threw, or returned something other than valid IR
or a list of files. Valid IR has the schema's shape and keeps the invariants every target
relies on (checkInvariants): component and export names every target can write, HTML
every target renders alike, and no code or document the compiler cannot analyse. The
message points at the first problems by JSON Pointer.
The compiler reports it instead of crashing, and leaves the plugin's step out: hooks receive a frozen module and frozen files, so a hook changes them by returning new ones, and a failed hook leaves nothing behind.
Fix the plugin, or remove it from the configuration.
Internal
UF9001 internal-error
Error. The compiler failed unexpectedly.
This is a bug in Unframework. Please open an issue with the file that triggered it: https://github.com/uxfront-com/unframework/issues/new/choose