mirror of
https://github.com/kennethnym/jrx.git
synced 2026-03-20 20:01:19 +00:00
Compare commits
7 Commits
00a5e83e8b
...
0.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c396d9f60 | |||
| 8680ef66c8 | |||
| 3827ccff23 | |||
| 650e2141d8 | |||
| 2d75600913 | |||
| 69a8a8fe67 | |||
| 4dcd184de0 |
11
.github/workflows/publish.yml
vendored
11
.github/workflows/publish.yml
vendored
@@ -4,6 +4,10 @@ on:
|
|||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
id-token: write # Required for OIDC
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
publish:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -15,12 +19,17 @@ jobs:
|
|||||||
|
|
||||||
- uses: oven-sh/setup-bun@v2
|
- uses: oven-sh/setup-bun@v2
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '24'
|
||||||
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|
||||||
- run: bun install
|
- run: bun install
|
||||||
|
|
||||||
- run: bun run build
|
- run: bun run build
|
||||||
|
|
||||||
- run: bun test
|
- run: bun test
|
||||||
|
|
||||||
- run: bun publish --access public --provenance
|
- run: bunx npm publish --provenance --access public
|
||||||
env:
|
env:
|
||||||
NPM_CONFIG_REGISTRY: https://registry.npmjs.org
|
NPM_CONFIG_REGISTRY: https://registry.npmjs.org
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
{
|
{
|
||||||
"name": "@nym.sh/jrx",
|
"name": "@nym.sh/jrx",
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"description": "JSX factory for json-render. Write JSX, get Spec JSON.",
|
"description": "JSX factory for json-render. Write JSX, get Spec JSON.",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/kennethnym/jrx"
|
||||||
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export { render } from "./render";
|
export { render } from "./render";
|
||||||
export { isJrxNode, JRX_NODE, FRAGMENT } from "./types";
|
export { isJrxElement, isJrxNode, JRX_NODE, FRAGMENT } from "./types";
|
||||||
export type { JrxNode, JrxComponent, RenderOptions } from "./types";
|
export type { JrxElement, JrxNode, JrxComponent, RenderOptions } from "./types";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type {
|
|||||||
ActionBinding,
|
ActionBinding,
|
||||||
VisibilityCondition,
|
VisibilityCondition,
|
||||||
} from "@json-render/core";
|
} from "@json-render/core";
|
||||||
import { JRX_NODE, FRAGMENT, type JrxNode } from "./types";
|
import { JRX_NODE, FRAGMENT, type JrxElement, type JrxNode } from "./types";
|
||||||
import type { JrxComponent } from "./types";
|
import type { JrxComponent } from "./types";
|
||||||
|
|
||||||
export { FRAGMENT as Fragment };
|
export { FRAGMENT as Fragment };
|
||||||
@@ -18,26 +18,26 @@ const RESERVED_PROPS = new Set([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalize a raw `children` value from JSX props into a flat array of JrxNodes.
|
* Normalize a raw `children` value from JSX props into a flat array of JrxElements.
|
||||||
* Handles: undefined, single node, nested arrays, and filters out nulls/booleans.
|
* Handles: undefined, single node, nested arrays, and filters out nulls/booleans.
|
||||||
*/
|
*/
|
||||||
function normalizeChildren(raw: unknown): JrxNode[] {
|
function normalizeChildren(raw: unknown): JrxElement[] {
|
||||||
if (raw == null || typeof raw === "boolean") return [];
|
if (raw == null || typeof raw === "boolean") return [];
|
||||||
|
|
||||||
if (Array.isArray(raw)) {
|
if (Array.isArray(raw)) {
|
||||||
const result: JrxNode[] = [];
|
const result: JrxElement[] = [];
|
||||||
for (const child of raw) {
|
for (const child of raw) {
|
||||||
if (child == null || typeof child === "boolean") continue;
|
if (child == null || typeof child === "boolean") continue;
|
||||||
if (Array.isArray(child)) {
|
if (Array.isArray(child)) {
|
||||||
result.push(...normalizeChildren(child));
|
result.push(...normalizeChildren(child));
|
||||||
} else {
|
} else {
|
||||||
result.push(child as JrxNode);
|
result.push(child as JrxElement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [raw as JrxNode];
|
return [raw as JrxElement];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,9 +62,9 @@ type JsxType = string | typeof FRAGMENT | JrxComponent;
|
|||||||
* Core factory — shared by `jsx` and `jsxs`.
|
* Core factory — shared by `jsx` and `jsxs`.
|
||||||
*
|
*
|
||||||
* If `type` is a function, it is called with props (like React calls
|
* If `type` is a function, it is called with props (like React calls
|
||||||
* function components). The function returns a JrxNode directly.
|
* function components). The function may return null/undefined.
|
||||||
*
|
*
|
||||||
* If `type` is a string or Fragment, a JrxNode is constructed inline.
|
* If `type` is a string or Fragment, a JrxElement is constructed inline.
|
||||||
*/
|
*/
|
||||||
function createNode(
|
function createNode(
|
||||||
type: JsxType,
|
type: JsxType,
|
||||||
@@ -73,6 +73,7 @@ function createNode(
|
|||||||
const p = rawProps ?? {};
|
const p = rawProps ?? {};
|
||||||
|
|
||||||
// Function component — call it, just like React does.
|
// Function component — call it, just like React does.
|
||||||
|
// May return null/undefined to render nothing.
|
||||||
if (typeof type === "function") {
|
if (typeof type === "function") {
|
||||||
return type(p);
|
return type(p);
|
||||||
}
|
}
|
||||||
@@ -104,7 +105,9 @@ export function jsx(
|
|||||||
key?: string,
|
key?: string,
|
||||||
): JrxNode {
|
): JrxNode {
|
||||||
const node = createNode(type, props);
|
const node = createNode(type, props);
|
||||||
if (key != null) node.key = String(key);
|
// Key is intentionally dropped when a component returns null/undefined —
|
||||||
|
// there is no element to attach it to.
|
||||||
|
if (node != null && key != null) node.key = String(key);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +121,7 @@ export function jsxs(
|
|||||||
key?: string,
|
key?: string,
|
||||||
): JrxNode {
|
): JrxNode {
|
||||||
const node = createNode(type, props);
|
const node = createNode(type, props);
|
||||||
if (key != null) node.key = String(key);
|
if (node != null && key != null) node.key = String(key);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +135,7 @@ export namespace JSX {
|
|||||||
[tag: string]: Record<string, unknown>;
|
[tag: string]: Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The type returned by JSX expressions. */
|
/** The type returned by JSX expressions (may be null/undefined). */
|
||||||
export type Element = JrxNode;
|
export type Element = JrxNode;
|
||||||
|
|
||||||
export interface ElementChildrenAttribute {
|
export interface ElementChildrenAttribute {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, it, expect } from "bun:test";
|
import { describe, it, expect } from "bun:test";
|
||||||
import { render } from "./render";
|
import { render } from "./render";
|
||||||
import { isJrxNode, FRAGMENT } from "./types";
|
import { isJrxElement, FRAGMENT } from "./types";
|
||||||
import { jsx, jsxs, Fragment } from "./jsx-runtime";
|
import { jsx, jsxs, Fragment } from "./jsx-runtime";
|
||||||
import {
|
import {
|
||||||
Stack,
|
Stack,
|
||||||
@@ -18,26 +18,26 @@ import {
|
|||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
describe("jsx factory", () => {
|
describe("jsx factory", () => {
|
||||||
it("jsx() with string type returns a JrxNode", () => {
|
it("jsx() with string type returns a JrxElement", () => {
|
||||||
const node = jsx("Card", { title: "Hello" });
|
const node = jsx("Card", { title: "Hello" });
|
||||||
expect(isJrxNode(node)).toBe(true);
|
expect(isJrxElement(node)).toBe(true);
|
||||||
expect(node.type).toBe("Card");
|
expect(node!.type).toBe("Card");
|
||||||
expect(node.props).toEqual({ title: "Hello" });
|
expect(node!.props).toEqual({ title: "Hello" });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("jsx() with component function resolves typeName", () => {
|
it("jsx() with component function resolves typeName", () => {
|
||||||
const node = jsx(Card, { title: "Hello" });
|
const node = jsx(Card, { title: "Hello" });
|
||||||
expect(isJrxNode(node)).toBe(true);
|
expect(isJrxElement(node)).toBe(true);
|
||||||
expect(node.type).toBe("Card");
|
expect(node!.type).toBe("Card");
|
||||||
expect(node.props).toEqual({ title: "Hello" });
|
expect(node!.props).toEqual({ title: "Hello" });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("jsxs() returns a JrxNode with children", () => {
|
it("jsxs() returns a JrxElement with children", () => {
|
||||||
const node = jsxs(Stack, {
|
const node = jsxs(Stack, {
|
||||||
children: [jsx(Text, { content: "A" }), jsx(Text, { content: "B" })],
|
children: [jsx(Text, { content: "A" }), jsx(Text, { content: "B" })],
|
||||||
});
|
});
|
||||||
expect(isJrxNode(node)).toBe(true);
|
expect(isJrxElement(node)).toBe(true);
|
||||||
expect(node.children).toHaveLength(2);
|
expect(node!.children).toHaveLength(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Fragment is the FRAGMENT symbol", () => {
|
it("Fragment is the FRAGMENT symbol", () => {
|
||||||
@@ -46,8 +46,8 @@ describe("jsx factory", () => {
|
|||||||
|
|
||||||
it("jsx() extracts key from third argument", () => {
|
it("jsx() extracts key from third argument", () => {
|
||||||
const node = jsx(Card, { title: "Hi" }, "my-key");
|
const node = jsx(Card, { title: "Hi" }, "my-key");
|
||||||
expect(node.key).toBe("my-key");
|
expect(node!.key).toBe("my-key");
|
||||||
expect(node.props).toEqual({ title: "Hi" });
|
expect(node!.props).toEqual({ title: "Hi" });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("jsx() extracts reserved props", () => {
|
it("jsx() extracts reserved props", () => {
|
||||||
@@ -64,26 +64,70 @@ describe("jsx factory", () => {
|
|||||||
watch,
|
watch,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(node.props).toEqual({ label: "Go" });
|
expect(node!.props).toEqual({ label: "Go" });
|
||||||
expect(node.visible).toEqual(vis);
|
expect(node!.visible).toEqual(vis);
|
||||||
expect(node.on).toEqual(on);
|
expect(node!.on).toEqual(on);
|
||||||
expect(node.repeat).toEqual(repeat);
|
expect(node!.repeat).toEqual(repeat);
|
||||||
expect(node.watch).toEqual(watch);
|
expect(node!.watch).toEqual(watch);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("jsx() handles null props", () => {
|
it("jsx() handles null props", () => {
|
||||||
const node = jsx("Divider", null);
|
const node = jsx("Divider", null);
|
||||||
expect(isJrxNode(node)).toBe(true);
|
expect(isJrxElement(node)).toBe(true);
|
||||||
expect(node.props).toEqual({});
|
expect(node!.props).toEqual({});
|
||||||
expect(node.children).toEqual([]);
|
expect(node!.children).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("jsx() filters null/boolean children", () => {
|
it("jsx() filters null/boolean children", () => {
|
||||||
const node = jsxs(Stack, {
|
const node = jsxs(Stack, {
|
||||||
children: [null, jsx(Text, { content: "A" }), false, undefined, true],
|
children: [null, jsx(Text, { content: "A" }), false, undefined, true],
|
||||||
});
|
});
|
||||||
expect(node.children).toHaveLength(1);
|
expect(node!.children).toHaveLength(1);
|
||||||
expect(node.children[0].type).toBe("Text");
|
expect(node!.children[0].type).toBe("Text");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Null / undefined component returns
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
describe("null/undefined component returns", () => {
|
||||||
|
it("jsx() with a component returning null produces null", () => {
|
||||||
|
const NullComponent = (_props: Record<string, unknown>) => null;
|
||||||
|
const node = jsx(NullComponent, {});
|
||||||
|
expect(node).toBeNull();
|
||||||
|
expect(isJrxElement(node)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("jsx() with a component returning undefined produces undefined", () => {
|
||||||
|
const UndefinedComponent = (_props: Record<string, unknown>) => undefined;
|
||||||
|
const node = jsx(UndefinedComponent, {});
|
||||||
|
expect(node).toBeUndefined();
|
||||||
|
expect(isJrxElement(node)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("key argument is ignored when component returns null", () => {
|
||||||
|
const NullComponent = (_props: Record<string, unknown>) => null;
|
||||||
|
const node = jsx(NullComponent, {}, "my-key");
|
||||||
|
expect(node).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("null-returning component children are filtered out", () => {
|
||||||
|
const NullComponent = (_props: Record<string, unknown>) => null;
|
||||||
|
const node = jsxs(Stack, {
|
||||||
|
children: [jsx(NullComponent, {}), jsx(Text, { content: "A" })],
|
||||||
|
});
|
||||||
|
expect(node!.children).toHaveLength(1);
|
||||||
|
expect(node!.children[0].type).toBe("Text");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("undefined-returning component children are filtered out", () => {
|
||||||
|
const UndefinedComponent = (_props: Record<string, unknown>) => undefined;
|
||||||
|
const node = jsxs(Stack, {
|
||||||
|
children: [jsx(Text, { content: "A" }), jsx(UndefinedComponent, {})],
|
||||||
|
});
|
||||||
|
expect(node!.children).toHaveLength(1);
|
||||||
|
expect(node!.children[0].type).toBe("Text");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -300,7 +300,15 @@ describe("state passthrough", () => {
|
|||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
describe("error handling", () => {
|
describe("error handling", () => {
|
||||||
it("throws for non-JrxNode input", () => {
|
it("throws for non-JrxElement input", () => {
|
||||||
expect(() => render({} as JrxNode)).toThrow(/expects a JrxNode/);
|
expect(() => render({} as JrxNode)).toThrow(/expects a JrxElement/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws when given null", () => {
|
||||||
|
expect(() => render(null)).toThrow(/expects a JrxElement/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws when given undefined", () => {
|
||||||
|
expect(() => render(undefined)).toThrow(/expects a JrxElement/);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { Spec, UIElement } from "@json-render/core";
|
import type { Spec, UIElement } from "@json-render/core";
|
||||||
import { FRAGMENT, type JrxNode, type RenderOptions, isJrxNode } from "./types";
|
import { FRAGMENT, type JrxElement, type JrxNode, type RenderOptions, isJrxElement } from "./types";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flatten a JrxNode tree into a json-render `Spec`.
|
* Flatten a JrxNode tree into a json-render `Spec`.
|
||||||
@@ -21,8 +21,8 @@ import { FRAGMENT, type JrxNode, type RenderOptions, isJrxNode } from "./types";
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export function render(node: JrxNode, options?: RenderOptions): Spec {
|
export function render(node: JrxNode, options?: RenderOptions): Spec {
|
||||||
if (!isJrxNode(node)) {
|
if (!isJrxElement(node)) {
|
||||||
throw new Error("render() expects a JrxNode produced by JSX.");
|
throw new Error("render() expects a JrxElement produced by JSX.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.type === FRAGMENT) {
|
if (node.type === FRAGMENT) {
|
||||||
@@ -66,12 +66,12 @@ function generateKey(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the children of a node, expanding fragments inline.
|
* Resolve the children of a node, expanding fragments inline.
|
||||||
* Returns an array of concrete (non-fragment) JrxNodes.
|
* Returns an array of concrete (non-fragment) JrxElements.
|
||||||
*/
|
*/
|
||||||
function expandChildren(children: JrxNode[]): JrxNode[] {
|
function expandChildren(children: JrxElement[]): JrxElement[] {
|
||||||
const result: JrxNode[] = [];
|
const result: JrxElement[] = [];
|
||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
if (!isJrxNode(child)) continue;
|
if (!isJrxElement(child)) continue;
|
||||||
if (child.type === FRAGMENT) {
|
if (child.type === FRAGMENT) {
|
||||||
// Recursively expand nested fragments
|
// Recursively expand nested fragments
|
||||||
result.push(...expandChildren(child.children));
|
result.push(...expandChildren(child.children));
|
||||||
@@ -83,11 +83,11 @@ function expandChildren(children: JrxNode[]): JrxNode[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively flatten a JrxNode into the elements map.
|
* Recursively flatten a JrxElement into the elements map.
|
||||||
* Returns the key assigned to this node.
|
* Returns the key assigned to this node.
|
||||||
*/
|
*/
|
||||||
function flattenNode(
|
function flattenNode(
|
||||||
node: JrxNode,
|
node: JrxElement,
|
||||||
elements: Record<string, UIElement>,
|
elements: Record<string, UIElement>,
|
||||||
counters: Map<string, number>,
|
counters: Map<string, number>,
|
||||||
usedKeys: Set<string>,
|
usedKeys: Set<string>,
|
||||||
|
|||||||
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");
|
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()`
|
* Created by the `jsx` / `jsxs` factory functions and consumed by `render()`
|
||||||
* which flattens the tree into a json-render `Spec`.
|
* 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` */
|
/** Brand symbol — always `JRX_NODE` */
|
||||||
$$typeof: typeof JRX_NODE;
|
$$typeof: typeof JRX_NODE;
|
||||||
|
|
||||||
@@ -38,7 +40,7 @@ export interface JrxNode {
|
|||||||
props: Record<string, unknown>;
|
props: Record<string, unknown>;
|
||||||
|
|
||||||
/** Child nodes */
|
/** Child nodes */
|
||||||
children: JrxNode[];
|
children: JrxElement[];
|
||||||
|
|
||||||
// -- Reserved / meta fields (extracted from JSX props) --
|
// -- Reserved / meta fields (extracted from JSX props) --
|
||||||
|
|
||||||
@@ -58,6 +60,14 @@ export interface JrxNode {
|
|||||||
watch: Record<string, ActionBinding | ActionBinding[]> | undefined;
|
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
|
// 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:
|
* A jrx component function. Works like a React function component:
|
||||||
* when used as a JSX tag (`<Card />`), the factory calls the function
|
* 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;
|
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 {
|
export function component(typeName: string): JrxComponent {
|
||||||
// Import createNodeFromString lazily to avoid circular dep
|
// Import createNodeFromString lazily to avoid circular dep
|
||||||
// (jsx-runtime imports types). Instead, we build the node inline.
|
// (jsx-runtime imports types). Instead, we build the node inline.
|
||||||
return (props: Record<string, unknown>) => {
|
return (props: Record<string, unknown>): JrxElement => {
|
||||||
return {
|
return {
|
||||||
$$typeof: JRX_NODE,
|
$$typeof: JRX_NODE,
|
||||||
type: typeName,
|
type: typeName,
|
||||||
@@ -110,21 +120,21 @@ function filterReserved(props: Record<string, unknown>): Record<string, unknown>
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeChildrenRaw(raw: unknown): JrxNode[] {
|
function normalizeChildrenRaw(raw: unknown): JrxElement[] {
|
||||||
if (raw == null || typeof raw === "boolean") return [];
|
if (raw == null || typeof raw === "boolean") return [];
|
||||||
if (Array.isArray(raw)) {
|
if (Array.isArray(raw)) {
|
||||||
const result: JrxNode[] = [];
|
const result: JrxElement[] = [];
|
||||||
for (const child of raw) {
|
for (const child of raw) {
|
||||||
if (child == null || typeof child === "boolean") continue;
|
if (child == null || typeof child === "boolean") continue;
|
||||||
if (Array.isArray(child)) {
|
if (Array.isArray(child)) {
|
||||||
result.push(...normalizeChildrenRaw(child));
|
result.push(...normalizeChildrenRaw(child));
|
||||||
} else {
|
} else {
|
||||||
result.push(child as JrxNode);
|
result.push(child as JrxElement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return [raw as JrxNode];
|
return [raw as JrxElement];
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -140,10 +150,13 @@ export interface RenderOptions {
|
|||||||
// Type guard
|
// Type guard
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export function isJrxNode(value: unknown): value is JrxNode {
|
export function isJrxElement(value: unknown): value is JrxElement {
|
||||||
return (
|
return (
|
||||||
typeof value === "object" &&
|
typeof value === "object" &&
|
||||||
value !== null &&
|
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