mirror of
https://github.com/kennethnym/jrx.git
synced 2026-03-21 20:31:19 +00:00
Support null and undefined in JrxNode type
Split JrxNode into JrxElement (concrete branded object) and JrxNode (JrxElement | null | undefined), mirroring React's ReactElement vs ReactNode pattern. Components can now return null/undefined to render nothing. - Rename isJrxNode to isJrxElement (deprecated alias kept) - Handle null-returning components in jsx/jsxs factories - Add tests for null/undefined returns, children filtering, render() rejection Co-authored-by: Ona <no-reply@ona.com>
This commit is contained in:
35
src/types.ts
35
src/types.ts
@@ -19,12 +19,14 @@ export const JRX_NODE = Symbol.for("jrx.node");
|
||||
export const FRAGMENT = Symbol.for("jrx.fragment");
|
||||
|
||||
/**
|
||||
* A node in the intermediate JSX tree.
|
||||
* A concrete element in the intermediate JSX tree.
|
||||
*
|
||||
* Created by the `jsx` / `jsxs` factory functions and consumed by `render()`
|
||||
* which flattens the tree into a json-render `Spec`.
|
||||
*
|
||||
* Analogous to React's `ReactElement`.
|
||||
*/
|
||||
export interface JrxNode {
|
||||
export interface JrxElement {
|
||||
/** Brand symbol — always `JRX_NODE` */
|
||||
$$typeof: typeof JRX_NODE;
|
||||
|
||||
@@ -38,7 +40,7 @@ export interface JrxNode {
|
||||
props: Record<string, unknown>;
|
||||
|
||||
/** Child nodes */
|
||||
children: JrxNode[];
|
||||
children: JrxElement[];
|
||||
|
||||
// -- Reserved / meta fields (extracted from JSX props) --
|
||||
|
||||
@@ -58,6 +60,14 @@ export interface JrxNode {
|
||||
watch: Record<string, ActionBinding | ActionBinding[]> | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Any value that can appear as a JSX child or component return value.
|
||||
*
|
||||
* Analogous to React's `ReactNode` — includes `null` and `undefined`
|
||||
* so components can conditionally render nothing.
|
||||
*/
|
||||
export type JrxNode = JrxElement | null | undefined;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// JrxComponent — a function usable as a JSX tag that maps to a type string
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -65,7 +75,7 @@ export interface JrxNode {
|
||||
/**
|
||||
* A jrx component function. Works like a React function component:
|
||||
* when used as a JSX tag (`<Card />`), the factory calls the function
|
||||
* with props and gets back a JrxNode.
|
||||
* with props and gets back a JrxNode (which may be null/undefined).
|
||||
*/
|
||||
export type JrxComponent = (props: Record<string, unknown>) => JrxNode;
|
||||
|
||||
@@ -85,7 +95,7 @@ export type JrxComponent = (props: Record<string, unknown>) => JrxNode;
|
||||
export function component(typeName: string): JrxComponent {
|
||||
// Import createNodeFromString lazily to avoid circular dep
|
||||
// (jsx-runtime imports types). Instead, we build the node inline.
|
||||
return (props: Record<string, unknown>) => {
|
||||
return (props: Record<string, unknown>): JrxElement => {
|
||||
return {
|
||||
$$typeof: JRX_NODE,
|
||||
type: typeName,
|
||||
@@ -110,21 +120,21 @@ function filterReserved(props: Record<string, unknown>): Record<string, unknown>
|
||||
return out;
|
||||
}
|
||||
|
||||
function normalizeChildrenRaw(raw: unknown): JrxNode[] {
|
||||
function normalizeChildrenRaw(raw: unknown): JrxElement[] {
|
||||
if (raw == null || typeof raw === "boolean") return [];
|
||||
if (Array.isArray(raw)) {
|
||||
const result: JrxNode[] = [];
|
||||
const result: JrxElement[] = [];
|
||||
for (const child of raw) {
|
||||
if (child == null || typeof child === "boolean") continue;
|
||||
if (Array.isArray(child)) {
|
||||
result.push(...normalizeChildrenRaw(child));
|
||||
} else {
|
||||
result.push(child as JrxNode);
|
||||
result.push(child as JrxElement);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return [raw as JrxNode];
|
||||
return [raw as JrxElement];
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -140,10 +150,13 @@ export interface RenderOptions {
|
||||
// Type guard
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function isJrxNode(value: unknown): value is JrxNode {
|
||||
export function isJrxElement(value: unknown): value is JrxElement {
|
||||
return (
|
||||
typeof value === "object" &&
|
||||
value !== null &&
|
||||
(value as JrxNode).$$typeof === JRX_NODE
|
||||
(value as JrxElement).$$typeof === JRX_NODE
|
||||
);
|
||||
}
|
||||
|
||||
/** @deprecated Use `isJrxElement` instead. */
|
||||
export const isJrxNode = isJrxElement;
|
||||
|
||||
Reference in New Issue
Block a user