Skip to content

@b9g/jsx-web-types — typed JSX intrinsic elements (#60) - #362

Open
brainkim wants to merge 2 commits into
mainfrom
jsx-web-types-update
Open

@b9g/jsx-web-types — typed JSX intrinsic elements (#60)#362
brainkim wants to merge 2 commits into
mainfrom
jsx-web-types-update

Conversation

@brainkim

@brainkim brainkim commented Jun 13, 2026

Copy link
Copy Markdown
Member

Introduces @b9g/jsx-web-types and wires it into Crank's JSX.IntrinsicElements (#60). Spec-driven, framework-agnostic types for HTML, SVG, and MathML intrinsic elements.

What it is

  • A generated .d.ts package: element → attributes/properties, ARIA, microdata/RDFa, global attributes, and a DOM event map. Each attribute carries its MDN description.
  • The standard types use native platform names (class, onclick). A separate camelCase variant (@b9g/jsx-web-types/camel-case) re-keys them for React/Inferno (className, htmlFor, …), with names sourced from React's own possibleStandardNames.js.
  • Crank's JSX.IntrinsicElements extends WebIntrinsicElements<…>, supplying its own intrinsic attributes (key/ref/prop:/attr:), children attribute, and object-style/class value overrides via the TPropOverrides parameter — so Crank's conventions live in the parameters, not the base types.

Everything is sourced — no hand-coded platform data

Generation is a reproducible function of authoritative sources:

Source What it provides How accessed
BCD (@mdn/browser-compat-data) element names, per-element attributes, global_attributes, deprecated/experimental status, mdn_url imported JSON
MDN pages human descriptions + code examples (the hover text) scraped with cheerio, cached in data/mdn-cache.json (offline-capable; network only on a cache miss)
aria-data ARIA attributes and their value enums imported JSON
TypeScript lib.dom.d.ts tag → DOM interface maps, DOM property types, the *EventMap interfaces parsed with ts-morph
W3C RDFa Core spec RDFa attributes scripts/parse-rdfa-w3c.tsdata/rdfa-attributes.json
MDN Microdata guide microdata attributes scripts/parse-microdata-mdn.tsdata/microdata-attributes.json
React possibleStandardNames.js the camelCase prop-name map (pinned to a release) scripts/parse-camel-case-names.tssrc/camel-case.generated.d.ts
(hand-written) only structural constants — the namespace names and the fixed XMLAttributes W3C set inline in the generator

Every .d.ts and data/*.json is regenerable from these and stamped do-not-edit; there is no hand-frozen platform data.

One known wrinkle: BCD omits fill and systemLanguage from svg.global_attributes despite their families being present. The generator promotes exactly those two (cross-referenced against BCD's per-element data); reported upstream as mdn/browser-compat-data#29912, and the workaround deletes itself once that lands.

Descriptions surface in editors (LSP)

Each attribute's MDN JSDoc reaches hover/completion. The prop types are composed so TypeScript preserves per-attribute doc comments (homomorphic mapped types; multi-namespace tags resolved by precedence rather than unioned), verified end-to-end through Crank JSX with the TypeScript language service — <div class>, <a href>, <label for>, etc. all show their descriptions.

Children

Void elements aren't specially typed: every element accepts children (the runtime ignores stray ones), so there's no void set or TIsVoid plumbing to maintain.

Tests

bun run test typechecks both src/ and the type-level test suite in tests/. Root tsc --noEmit is clean.

🤖 Generated with Claude Code

@brainkim brainkim mentioned this pull request Jun 14, 2026
7 tasks
@brainkim brainkim changed the title WIP: @b9g/jsx-web-types — typed JSX intrinsic elements (#60) @b9g/jsx-web-types — typed JSX intrinsic elements (#60) Jun 23, 2026
@brainkim
brainkim marked this pull request as ready for review June 23, 2026 19:12
Comment thread packages/jsx-web-types/scripts/generate.ts Outdated
@brainkim
brainkim force-pushed the jsx-web-types-update branch 3 times, most recently from 8e825dd to aef0618 Compare July 14, 2026 06:53
Comment thread packages/jsx-web-types/README.md
Comment thread src/crank.ts

export interface IntrinsicElements {
[tag: string]: any;
// Crank-specific prop value conventions, layered onto the platform types

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is pretty cool. The design makes me feel like a cat bathing in the sun.

Comment thread test/copy-prop.tsx
type="text"
copy={force ? false : "value"}
value={value}
// @ts-expect-error - handler types the event as InputEvent; the native oninput prop uses the base Event

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

why though?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The handler prop derives its event type from lib.dom’s GlobalEventHandlersEventMap, and lib.dom types oninput as the base Event, not InputEvent (long-standing quirk — the runtime fires an InputEvent, but the static type is widened). So a callback annotated (ev: InputEvent) is not assignable to the (ev: Event) => … the prop expects, hence the @ts-expect-error. The test pins that gap so we notice if the generated types ever start narrowing it.

brainkim added a commit that referenced this pull request Jul 16, 2026
Per review on #362: acronyms retain their case (acrocase.org), so
resolveDomLib → resolveDOMLib, mdnUrl → mdnURL, isFragmentUrl →
isFragmentURL, formattedHtml → formattedHTML. The mdnURL locals now match
the mdnURL object keys they feed, which collapse to shorthand.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
brainkim and others added 2 commits August 11, 2026 18:47
Adds the @b9g/jsx-web-types workspace package and wires it into Crank's
JSX.IntrinsicElements, giving first-class typing for HTML, SVG, and MathML
intrinsic elements and their attributes.

Element and global-attribute data is generated from BCD and MDN rather than
hand-maintained, with a corrections layer covering the SVG presentation and
MathML global-attribute gaps upstream still has. Unknown intrinsic tags now
error, not just unknown attributes.
Per review on #362: acronyms retain their case (acrocase.org), so
resolveDomLib → resolveDOMLib, mdnUrl → mdnURL, isFragmentUrl →
isFragmentURL, formattedHtml → formattedHTML. The mdnURL locals now match
the mdnURL object keys they feed, which collapse to shorthand.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@brainkim
brainkim force-pushed the jsx-web-types-update branch from 2e1b3a0 to 29ab3b4 Compare August 11, 2026 22:49
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