Skip to content

Helpers: new fromStaticMarkup, fallback to react-dom/server if needed#613

Merged
kane50613 merged 4 commits intomasterfrom
feat/from-static-markup
Mar 28, 2026
Merged

Helpers: new fromStaticMarkup, fallback to react-dom/server if needed#613
kane50613 merged 4 commits intomasterfrom
feat/from-static-markup

Conversation

@kane50613
Copy link
Copy Markdown
Owner

@kane50613 kane50613 commented Mar 28, 2026

Summary by CodeRabbit

  • New Features

    • Added fromStaticMarkup to parse static HTML/SVG into Takumi node trees and extract stylesheets.
    • JSX rendering now falls back to React DOM server rendering when hooks or context are present.
  • Chores

    • Added runtime dependency on ultrahtml.
    • Declared react-dom as an optional peer dependency.
    • Updated example to use a different GitHub SVG icon.
  • Tests

    • Added a test covering the server-rendering fallback with context and state.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Mar 28, 2026

🦋 Changeset detected

Latest commit: b44557b

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 28, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 83642609-2017-4837-aa7c-52d858e39b1c

📥 Commits

Reviewing files that changed from the base of the PR and between a8732a7 and b44557b.

⛔ Files ignored due to path filters (6)
  • bun.lock is excluded by !**/*.lock
  • example/twitter-images/output/500-stars.png is excluded by !**/*.png
  • example/twitter-images/output/github-social-preview.png is excluded by !**/*.png
  • example/twitter-images/output/og-image.png is excluded by !**/*.png
  • example/twitter-images/output/package-og-image.png is excluded by !**/*.png
  • example/twitter-images/output/x-post-image.png is excluded by !**/*.png
📒 Files selected for processing (9)
  • example/twitter-images/components/package-og-image.tsx
  • example/twitter-images/output/500-stars@2x.webp
  • example/twitter-images/output/github-social-preview@2x.webp
  • example/twitter-images/output/og-image@2x.webp
  • example/twitter-images/output/package-og-image@2x.webp
  • example/twitter-images/output/x-post-image@2x.webp
  • example/twitter-images/package.json
  • takumi-helpers/src/jsx/jsx.ts
  • takumi-helpers/test/jsx/jsx-processing.test.tsx

📝 Walkthrough

Walkthrough

Adds a static HTML/SVG-to-Takumi converter (fromStaticMarkup), removes internal React runtime/dispatcher interop, and implements a dynamic react-dom/server fallback for JSX that uses hooks or contains React context providers; updates deps, build and tests accordingly.

Changes

Cohort / File(s) Summary
Changesets
\.changeset/blue-tips-punch.md, \.changeset/weak-horses-fail.md
Added two changeset entries: a patch noting a react-dom/server fallback and a minor bump announcing the new fromStaticMarkup API.
Package manifest
takumi-helpers/package.json
Added runtime dependency ultrahtml; added react-dom to peerDependencies and marked it optional via peerDependenciesMeta.
JSX core
takumi-helpers/src/jsx/jsx.ts
Removed React dispatcher/context integration; added dynamic imports for react and react-dom/server; fromJsx falls back to server render on hook/provider-related errors; re-exports markup module; adjusted HtmlProps typing and void-element checks.
Static markup parser
takumi-helpers/src/jsx/markup.ts
New module exporting fromStaticMarkup and related types; parses HTML/SVG via ultrahtml into Takumi Node[] and extracts stylesheet strings; converts images, svgs, text, and container nodes.
React interop removal
takumi-helpers/src/jsx/react.ts
Deleted the React runtime/dispatcher interop layer, type guards, and context/dispatcher utilities.
Utilities / types
takumi-helpers/src/jsx/utils.ts
Exported ReactElementLike type and changed isHtmlVoidElement signature to accept a type: string.
Tests
takumi-helpers/test/jsx/jsx-processing.test.tsx
Expanded tests: added a case verifying react-dom/server fallback for a component using context and hooks; minor assertion added for SVG-based ImageNode.
Tooling & build config
takumi-helpers/tsconfig.json, takumi-helpers/tsdown.config.ts
Added "types": ["bun"] to tsconfig; prevented bundling of react-dom and react-dom/server in tsdown config.
Example changes
example/twitter-images/components/package-og-image.tsx, example/twitter-images/package.json
Replaced GitHub icon import with SiGithub from @icons-pack/react-simple-icons and added that dependency in the example package.json.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.35% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main changes: introducing a new fromStaticMarkup API and adding a fallback to react-dom/server when rendering with hooks, both of which are core to the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/from-static-markup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

Docs preview is ready: https://feat-from-static-markup.preview.takumi.kane.tw

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@takumi-helpers/src/jsx/jsx.ts`:
- Around line 211-244: containsReactContextProvider is incorrectly doing a raw
pre-scan that eagerly iterates generic iterables and descends through unresolved
wrappers (Promises, functions, memo, forwardRef), which consumes single-use
generators and misses providers introduced after rendering; stop doing that.
Change containsReactContextProvider to only check for a direct React context
provider node (inspect isValidElement and element.type.$$typeof ===
Symbol.for("react.context") and return false for iterables / Promises / non-HTML
components) and do NOT recursively iterate generic iterables or attempt to
resolve wrappers; then move the fallback/provider-detection that needs
rendered/awaited resolution into processReactElement where wrappers are actually
resolved (and where collectIterable can safely consume iterables), referencing
containsReactContextProvider, processReactElement, getElementChildren, and
collectIterable to implement the safer detection after rendering/awaiting.
- Around line 335-338: The current use of nullish coalescing turns an explicit
"no presets" into the default set; change the call to fromStaticMarkup to
preserve an explicitly provided presets value (including false) by using
presence-checking logic instead of ?? — e.g. set defaultStyles to
options.presets when the caller provided the presets property (so false stays
false), otherwise fall back to defaultStylePresets; update the object passed to
fromStaticMarkup (where defaultStyles is set) and keep references to
fromStaticMarkup, defaultStylePresets, and FromStaticMarkupOptions intact.
- Around line 234-239: The type check currently only detects React Context
objects by comparing element.type.$$typeof to Symbol.for("react.context"),
missing Context.Provider nodes; update the conditional that inspects
element.type and element.type.$$typeof to also accept
Symbol.for("react.provider") so Provider elements are routed to
renderWithReactDomServer instead of falling through to fromJsxInternal; locate
the check around element.type, $$typeof and adjust the comparison to include
both Symbol.for("react.context") and Symbol.for("react.provider") so Provider
detection is handled correctly.

In `@takumi-helpers/src/jsx/markup.ts`:
- Around line 271-273: The cssPropertyToJsProperty function currently
camel-cases CSS custom properties and breaks names like `--foo-bar`; update
cssPropertyToJsProperty to first detect properties that start with `--` and
return them unchanged, otherwise continue to perform the existing
hyphen-to-camelCase conversion (the existing regexp replace) so all non-custom
properties are converted as before.
- Around line 169-184: The current logic treats empty children arrays as
"onlyTextChildren" (because Array.every returns true for empty arrays), causing
empty elements to be emitted as empty text nodes; update the conditional that
decides between producing text(...) versus container(...) so it requires there
to be at least one child node (e.g., change the decision that uses
onlyTextChildren to also check children.nodes.length > 0) — modify the logic
around the onlyTextChildren variable and the return branch that calls
text({...}) / container({...}) so empty elements fall through to container(...)
instead of text("").
- Around line 108-143: The early isHtmlVoidElement({ type: element.name, props:
{} } as ReactElementLike) check is dropping <br> and <img> before the explicit
branches run; move or adjust that void-element check so <br> and <img> are
handled first: check element.name === "br" and element.name === "img" (and call
extractStaticNodeMetadata/parseDimension/text/image as shown) before calling
isHtmlVoidElement, or change the void check to exclude "br" and "img" so those
conversion branches execute.
🪄 Autofix (Beta)

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 282f3db5-9bd4-45ec-80b1-181114a5c74f

📥 Commits

Reviewing files that changed from the base of the PR and between b9841a7 and 36e3c0a.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • .changeset/blue-tips-punch.md
  • .changeset/weak-horses-fail.md
  • takumi-helpers/package.json
  • takumi-helpers/src/jsx/jsx.ts
  • takumi-helpers/src/jsx/markup.ts
  • takumi-helpers/src/jsx/react.ts
  • takumi-helpers/src/jsx/utils.ts
  • takumi-helpers/test/jsx/jsx-processing.test.tsx
  • takumi-helpers/tsconfig.json
  • takumi-helpers/tsdown.config.ts
💤 Files with no reviewable changes (1)
  • takumi-helpers/src/jsx/react.ts

Comment thread takumi-helpers/src/jsx/jsx.ts Outdated
Comment thread takumi-helpers/src/jsx/jsx.ts Outdated
Comment thread takumi-helpers/src/jsx/jsx.ts
Comment thread takumi-helpers/src/jsx/markup.ts Outdated
Comment thread takumi-helpers/src/jsx/markup.ts
Comment thread takumi-helpers/src/jsx/markup.ts
@kane50613 kane50613 force-pushed the feat/from-static-markup branch from a3b2acc to 36e3c0a Compare March 28, 2026 16:04
Repository owner deleted a comment from coderabbitai Bot Mar 28, 2026
@kane50613 kane50613 merged commit 7ff886b into master Mar 28, 2026
25 of 26 checks passed
@kane50613 kane50613 deleted the feat/from-static-markup branch March 28, 2026 16:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant