Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ claude-research.key
local-dev-toolshed.log
local-dev-shell.log
tools/ralph/logs/
tools/ralph/smoketest/
7 changes: 2 additions & 5 deletions deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@
"initialize-db": "./tasks/initialize-db.sh"
},
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@commontools/html",
"types": [
"./packages/static/assets/types/jsx.d.ts"
],
"jsx": "react-jsxdev",
"lib": [
"deno.ns",
"dom",
Expand Down Expand Up @@ -96,10 +97,6 @@
]
},
"imports": {
"react": "npm:react@^18.3.1",
"react-dom": "npm:react-dom@^18.3.1",
"@types/react": "npm:@types/react@^18.3.1",
"@babel/standalone": "npm:@babel/standalone@^7.28.2",
"commontools": "./packages/api/index.ts",
"core-js/proposals/explicit-resource-management": "https://esm.sh/core-js/proposals/explicit-resource-management",
"@astral/astral": "./packages/vendor-astral/mod.ts",
Expand Down
371 changes: 159 additions & 212 deletions deno.lock

Large diffs are not rendered by default.

45 changes: 34 additions & 11 deletions docs/specs/recipe-construction/rollout-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,36 @@

- [ ] Disable ShadowRef/unsafe_ and see what breaks, ideally remove it
- [ ] Update Cell API types to already unify them
- [ ] Create a CellLike<> type with a symbol based brand, with the value be
- [ ] Create an `AnyCell<>` type with a symbol based brand, with the value be
`Record<string, boolean>`
- [ ] Factor out parts of the cell interfaces along reading, writing, .send
(for stream-like) and derives (which is currently just .map)
- [ ] Define `OpaqueRef<>`, `Cell<>` and `Stream<>` by using these factored
- [ ] Define `OpaqueCell<>`, `Cell<>` and `Stream<>` by using these factored
out parts, combined with the brand set to `{ opaque: true, read: false,
write: false, stream: false }` for `OpaqueRef`, `{ opaque: false, read:
true, write: true, stream: true }` for `Cell`, and `{ opaque: false, read:
false, write: false, stream: true }` for `Stream`. We can go ahead and add
ReadonlyCell and WriteonlyCell accordingly as well.
- [ ] Add `ComparableCell<>` that is all `false` above
- [ ] Alias `OpaqueCell<>` to `OpaqueRef<>` (maintain backward compatibility)
- [ ] For `OpaqueRef` we keep the proxy behavior, i.e. each key is an
`OpaqueRef` again.
- [ ] Simplify most wrap/unwrap types to use `CellLike`.
false, write: false, stream: true }` for `Stream`.
- [ ] Add `ComparableCell<>` that is all `false` above.
- [ ] Add `ReadonlyCell` and `WriteonlyCell`.
- [ ] Make `OpaqueRef` a variant of `OpaqueCell` with the current proxy
behavior, i.e. each key is an `OpaqueRef` again. That's just for now, until
the AST does a .key transformation under the hood.
- [ ] Update `CellLike` to be based on `AnyCell` but allow nesting.
- [ ] `Opaque<T>` accepts `T` or any `CellLike<T>` at any nesting level
- [ ] Simplify most wrap/unwrap types to use `CellLike`. We need
- [ ] "Accept any T where any sub part of T can be wrapped in one or more
`AnyCell`" (for inputs to node factories)
- [ ] "Strip any `AnyCell` from T and then wrap it in OpaqueRef<>" (for
outputs of node factories, where T is the output of the inner function)
- [ ] Make passing the output of the second into the first work. Tricky
because we're doing almost opposite expansions on the type.
- [ ] Add ability to create a cell without a link yet.
- [ ] Change constructor for RegularCell to make link optional
- [ ] Add .for method to set a cause (within current context)
- [ ] second parameter to make it optional/flexible:
- [ ] ignores the .for if link already exists
- [ ] adds extension if cause already exists (see tracker below)
- [ ] Make .key work even if there is no cause yet.
- [ ] Add some method to force creation of cause, which errors if in
non-handler context and no other information was given (as e.g. deriving
nodes, which do have ids, after asking for them -- this walks the graph up
Expand All @@ -31,8 +40,22 @@
isn't there, e.g. because we need to create a link to the cell (when passed
into `anotherCell.set()` for example). We want to encourage .for use in
ambiguous cases.
- First merge of OpaqueRef and RegularCell
- [ ] First merge of OpaqueRef and RegularCell
- [ ] Add methods that allow linking to node invocations
- [ ] `setPreExisting` can be deprecated (used in toOpaqueRef which itself
can go away, see below)
- [ ] `setDefault` can be deprecated
- [ ] `setSchema` is tricky (asSchema is cleaner). Let's support it for now,
but only if the cause isn't set yet.
- [ ] `connect` copy over and add a direction field, so can distinguish
where this node is used as input vs where the passed node is an input to
this node.
- [ ] `export` make the analogous version, if link is present use that as
`external`.
- [ ] `map` and `mapWithPattern`: Copy over
- [ ] `toJSON` return `null` when no link otherwise what Cell does.
- [ ] No need for `toOpaqueRef` anymore, since all cells are now also
OpaqueRef. So remove all that.
- [ ] Call that for returned value in lift/handler, with a .for("assigned
variable of property", true)
- [ ] For now treat result as recipe, but it should be one where all nodes
Expand Down Expand Up @@ -80,7 +103,7 @@
- [ ] Add `.remove` and `.removeAll` which removes the element matching the
parameter from the list.
- [ ] Add overload to `.key` that accepts an array of keys

- [ ] Make name parameter in recipe optional

## Planned Future Work

Expand Down
10 changes: 3 additions & 7 deletions packages/html/deno.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,13 @@
},
"exports": {
".": "./src/index.ts",
"./utils": "./src/utils.ts"
"./utils": "./src/utils.ts",
"./jsx-runtime": "./src/jsx-runtime.ts",
"./jsx-dev-runtime": "./src/jsx-dev-runtime.ts"
},
"imports": {
"htmlparser2": "npm:htmlparser2",
"domhandler": "npm:domhandler",
"dom-serializer": "npm:dom-serializer"
},

"compilerOptions": {
"jsx": "react",
"jsxFactory": "h",
"jsxFragmentFactory": "h.fragment"
}
}
80 changes: 80 additions & 0 deletions packages/html/src/jsx-dev-runtime.ts
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 };
72 changes: 72 additions & 0 deletions packages/html/src/jsx-runtime.ts
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 };
Loading
Loading