-
Notifications
You must be signed in to change notification settings - Fork 9
feat: Migrate @commontools/html into a jsx-runtime implementation. #1958
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| /** | ||
| * JSX development runtime for @commontools/html | ||
| * | ||
| * This module provides the JSX development runtime implementation compatible with | ||
| * TypeScript's "jsx": "react-jsxdev" configuration. | ||
| * | ||
| * The development runtime includes additional debugging information like source | ||
| * file paths and line numbers, though our current implementation doesn't use these yet. | ||
| * | ||
| * @module jsx-dev-runtime | ||
| */ | ||
|
|
||
| import { h } from "@commontools/api"; | ||
| import type { RenderNode, VNode } from "@commontools/api"; | ||
|
|
||
| /** | ||
| * Props type for JSX elements in development mode, including children and debug info | ||
| */ | ||
| export interface JSXDevProps { | ||
| children?: RenderNode | RenderNode[]; | ||
| key?: string | number; | ||
| [prop: string]: any; | ||
| } | ||
|
|
||
| /** | ||
| * Source location information for debugging | ||
| */ | ||
| export interface Source { | ||
| fileName: string; | ||
| lineNumber: number; | ||
| columnNumber: number; | ||
| } | ||
|
|
||
| /** | ||
| * Creates a VNode for a JSX element with development-time debugging information. | ||
| * | ||
| * This function is used by the JSX automatic runtime in development mode. | ||
| * It accepts additional parameters for debugging (__source, __self) which can be | ||
| * used to provide better error messages and developer experience. | ||
| * | ||
| * @param type - The element type (string for HTML/SVG, function for components) | ||
| * @param props - Element properties including children | ||
| * @param key - Optional key for list reconciliation | ||
| * @param isStaticChildren - Whether children are static (unused in our implementation) | ||
| * @param __source - Source location information for debugging | ||
| * @param __self - Reference to the component instance (unused in our implementation) | ||
| * @returns A virtual DOM node | ||
| */ | ||
| export function jsxDEV( | ||
| type: string | ((props: any) => VNode), | ||
| props: JSXDevProps | null, | ||
| _key?: string | number, | ||
| _isStaticChildren?: boolean, | ||
| __source?: Source, | ||
| __self?: any, | ||
| ): VNode { | ||
| const { children, ...restProps } = props ?? {}; | ||
|
|
||
| // Convert children to array format expected by h() | ||
| const childArray = children === undefined | ||
| ? [] | ||
| : Array.isArray(children) | ||
| ? children | ||
| : [children]; | ||
|
|
||
| // In the future, we could use __source to provide better error messages | ||
| // or enhance debugging capabilities. For now, we just create the VNode. | ||
| return h(type, restProps, ...childArray); | ||
| } | ||
|
|
||
| /** | ||
| * Fragment component for grouping elements without adding DOM nodes. | ||
| * | ||
| * Used when you write <></> or <React.Fragment> in JSX. | ||
| * Renders as a "common-fragment" element in the virtual DOM. | ||
| */ | ||
| export const Fragment = h.fragment; | ||
|
|
||
| // Type exports | ||
| export type { RenderNode, VNode }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| /** | ||
| * JSX automatic runtime for @commontools/html | ||
| * | ||
| * This module provides the JSX runtime implementation compatible with | ||
| * TypeScript's "jsx": "react-jsx" configuration. | ||
| * | ||
| * @module jsx-runtime | ||
| */ | ||
|
|
||
| import { h } from "@commontools/api"; | ||
| import type { RenderNode, VNode } from "@commontools/api"; | ||
|
|
||
| /** | ||
| * Props type for JSX elements, including children | ||
| */ | ||
| export interface JSXProps { | ||
| children?: RenderNode | RenderNode[]; | ||
| key?: string | number; | ||
| [prop: string]: any; | ||
| } | ||
|
|
||
| /** | ||
| * Creates a VNode for a JSX element. | ||
| * | ||
| * This is the core function used by the JSX automatic runtime for creating elements. | ||
| * It handles both HTML/SVG elements (string types) and component functions. | ||
| * | ||
| * @param type - The element type (string for HTML/SVG, function for components) | ||
| * @param props - Element properties including children | ||
| * @param key - Optional key for list reconciliation (currently unused but part of JSX spec) | ||
| * @returns A virtual DOM node | ||
| */ | ||
| export function jsx( | ||
| type: string | ((props: any) => VNode), | ||
| props: JSXProps | null, | ||
| _key?: string | number, | ||
| ): VNode { | ||
| const { children, ...restProps } = props ?? {}; | ||
|
|
||
| // Convert children to array format expected by h() | ||
| const childArray = children === undefined | ||
| ? [] | ||
| : Array.isArray(children) | ||
| ? children | ||
| : [children]; | ||
|
|
||
| return h(type, restProps, ...childArray); | ||
| } | ||
|
|
||
| /** | ||
| * Creates a VNode for a JSX element with static children. | ||
| * | ||
| * The TypeScript compiler uses this when it can determine that children are static. | ||
| * For our implementation, it's identical to jsx() since we don't optimize for static children. | ||
| * | ||
| * @param type - The element type (string for HTML/SVG, function for components) | ||
| * @param props - Element properties including children | ||
| * @param key - Optional key for list reconciliation | ||
| * @returns A virtual DOM node | ||
| */ | ||
| export const jsxs = jsx; | ||
|
|
||
| /** | ||
| * Fragment component for grouping elements without adding DOM nodes. | ||
| * | ||
| * Used when you write <></> or <React.Fragment> in JSX. | ||
| * Renders as a "common-fragment" element in the virtual DOM. | ||
| */ | ||
| export const Fragment = h.fragment; | ||
|
|
||
| // Type exports | ||
| export type { RenderNode, VNode }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| /** | ||
| * Tests for the JSX development runtime | ||
| * | ||
| * These tests verify that @commontools/html provides a development runtime | ||
| * compatible with TypeScript's "jsx": "react-jsxdev" configuration. | ||
| */ | ||
|
|
||
| import { describe, it } from "@std/testing/bdd"; | ||
| import * as assert from "./assert.ts"; | ||
|
|
||
| import { Fragment, jsxDEV } from "../src/jsx-dev-runtime.ts"; | ||
|
|
||
| describe("JSX development runtime", () => { | ||
| it("jsxDEV() creates a simple element", () => { | ||
| const element = jsxDEV("div", { className: "test" }); | ||
|
|
||
| assert.matchObject(element, { | ||
| type: "vnode", | ||
| name: "div", | ||
| props: { className: "test" }, | ||
| children: [], | ||
| }); | ||
| }); | ||
|
|
||
| it("jsxDEV() creates an element with children", () => { | ||
| const element = jsxDEV("div", { | ||
| children: [jsxDEV("p", { children: "Hello" })], | ||
| }); | ||
|
|
||
| assert.matchObject(element, { | ||
| type: "vnode", | ||
| name: "div", | ||
| children: [ | ||
| { | ||
| type: "vnode", | ||
| name: "p", | ||
| children: ["Hello"], | ||
| }, | ||
| ], | ||
| }); | ||
| }); | ||
|
|
||
| it("jsxDEV() accepts debug parameters", () => { | ||
| const element = jsxDEV( | ||
| "div", | ||
| { children: "Test" }, | ||
| "test-key", | ||
| false, | ||
| { | ||
| fileName: "test.tsx", | ||
| lineNumber: 42, | ||
| columnNumber: 10, | ||
| }, | ||
| undefined, | ||
| ); | ||
|
|
||
| assert.matchObject(element, { | ||
| type: "vnode", | ||
| name: "div", | ||
| children: ["Test"], | ||
| }); | ||
| }); | ||
|
|
||
| it("jsxDEV() handles null props", () => { | ||
| const element = jsxDEV("div", null); | ||
|
|
||
| assert.matchObject(element, { | ||
| type: "vnode", | ||
| name: "div", | ||
| props: {}, | ||
| children: [], | ||
| }); | ||
| }); | ||
|
|
||
| it("jsxDEV() handles component functions", () => { | ||
| const MyComponent = ({ name }: { name: string }) => | ||
| jsxDEV("div", { children: `Hello, ${name}` }); | ||
|
|
||
| const element = jsxDEV(MyComponent, { name: "World" }); | ||
|
|
||
| assert.matchObject(element, { | ||
| type: "vnode", | ||
| name: "div", | ||
| children: ["Hello, World"], | ||
| }); | ||
| }); | ||
|
|
||
| it("Fragment creates a common-fragment element", () => { | ||
| const fragment = Fragment({ | ||
| children: [ | ||
| jsxDEV("p", { children: "Paragraph 1" }), | ||
| jsxDEV("p", { children: "Paragraph 2" }), | ||
| ], | ||
| }); | ||
|
|
||
| assert.matchObject(fragment, { | ||
| type: "vnode", | ||
| name: "common-fragment", | ||
| children: [ | ||
| { | ||
| type: "vnode", | ||
| name: "p", | ||
| children: ["Paragraph 1"], | ||
| }, | ||
| { | ||
| type: "vnode", | ||
| name: "p", | ||
| children: ["Paragraph 2"], | ||
| }, | ||
| ], | ||
| }); | ||
| }); | ||
|
|
||
| it("jsxDEV() with static children flag", () => { | ||
| const element = jsxDEV( | ||
| "ul", | ||
| { | ||
| children: [ | ||
| jsxDEV("li", { children: "Item 1" }), | ||
| jsxDEV("li", { children: "Item 2" }), | ||
| ], | ||
| }, | ||
| undefined, | ||
| true, // isStaticChildren | ||
| ); | ||
|
|
||
| assert.matchObject(element, { | ||
| type: "vnode", | ||
| name: "ul", | ||
| children: [ | ||
| { | ||
| type: "vnode", | ||
| name: "li", | ||
| children: ["Item 1"], | ||
| }, | ||
| { | ||
| type: "vnode", | ||
| name: "li", | ||
| children: ["Item 2"], | ||
| }, | ||
| ], | ||
| }); | ||
| }); | ||
| }); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The JSX runtime ignores the
_keyargument, so keyed JSX elements lose their keys when compiled withjsx: "react-jsx". Without the key, list reconciliation and any component logic that depends on it will misbehave. Please merge_keyback into the props before callingh.Prompt for AI agents