Skip to content

Commit 63807bf

Browse files
committed
Address #49 code review: gitignore stub, reserved idents, collision/leak/polarity guards
Fixes the four review findings on the #39 views work, all with probe-derived golden coverage (new case views-polarity locks the contract edges): 1. CI-red (vendor stub untracked): the vendor-views fixture's @floating-ui stub was swallowed by the global node_modules/ ignore, so CI checked out a tree without it, the vendor trial found nothing, and the golden diverged. Added a gitignore exception for test/golden/cases/*/node_modules/ and committed the stub. The Tainted rejection path is now actually exercised in CI. 2. Reserved-word literals compiled-broken: emit's lowerIdent used a hand-rolled reserved regex that missed 'true'/'lazy' (emitted "let true: t", rejected by ReScript) and disagreed with extract's note. Unified ONE RESCRIPT_RESERVED set in stdlib-types, imported by both extract and emit, so output is true_ / lazy_ and the prop note always matches the emitted code. 3. Silent variant drops now flag (all-cases-or-flag): two arms producing the same constructor ident ('trap-focus' vs 'trapFocus', or two anonymous fns) now REJECT the whole module (prop stays review) instead of emit's seen-dedup quietly dropping the second variant. Anonymous __type fn/array members are named Fn / derived from the element record name, so no fromType/fromTypes leak reaches the output (real reach: react-day-picker Disabled.fromType is now Disabled.fromFn). 4. Receive-position regression: construct-only views (literal/none arms) reaching a callback PARAM produced an uninspectable black box. Added produce/receive POLARITY tracking that flips at every fn boundary (params !P, return P) and no longer leaks ctx.inFnReturn into nested params; construct-only views are gated on a genuinely consumer-produced position, else the honest 'a salvage / flag. Minors: trialVendorRecord now fully rolls back minted names + ctx.typeVars on a failed trial (no Rect -> Rect2 pollution); dropped its unused depth param; documented the vendor gate's real scope (@floating-ui + styled-components). 39 goldens match + compile; consumer-side usage compile-verified; base-ui 194/1/0 unchanged; bench 8/8 PASS, no DEGRADED. TYPE_MAPPING + CHANGELOG.
1 parent 2f0f5b4 commit 63807bf

24 files changed

Lines changed: 229 additions & 59 deletions

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# dependencies
22
node_modules/
3+
# ...except golden-fixture vendor stubs (self-contained test inputs, not real deps)
4+
!test/golden/cases/*/node_modules/
5+
!test/golden/cases/*/node_modules/**
36

47
# scratch install cache (versions fetched from npm for generation)
58
.bindgen-cache/

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
3232
imperfections (an unflagged `=> string` fake can no longer hide inside a
3333
view). `isDomNodeType` is lib.dom-guarded (a vendor type merely named
3434
*Element no longer collapses into `Dom.element`). `DOMRectList` joined the
35-
WebTypes sink.
35+
WebTypes sink. Review hardening: construct-only views are gated to produce-positions (callback params keep the `'a` salvage), ident-colliding/`__type` arms reject the module instead of silently dropping a variant, reserved-word literal constants use the shared reserved set (note matches code), and a failed vendor-record trial fully rolls back minted names + type vars.
3636
- **Render-prop function form bindable via zero-cost wrapper** (#46): a
3737
`render?: ReactElement | ((props, state) => ReactElement)` prop stays
3838
`React.element` (the common case is unchanged), and the component module now

benchmark/baselines/_base-ui-components_react/bindings/ComboboxPositioner.res

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ external renderFn: ((PositionerSharedTypes.htmlProps, PositionerSharedTypes.comb
33

44
@module("@base-ui-components/react") @scope("Autocomplete") @react.component
55
external make: (
6-
~anchor: DistTypes.Anchor.t=?, // ⓘ was `Element | VirtualElement | RefObject<Element> | (() => Element | VirtualElement)` — opaque; build with Anchor.fromElement / Anchor.fromVirtualElement / Anchor.fromRefObject / Anchor.fromType
6+
~anchor: DistTypes.Anchor.t=?, // ⓘ was `Element | VirtualElement | RefObject<Element> | (() => Element | VirtualElement)` — opaque; build with Anchor.fromElement / Anchor.fromVirtualElement / Anchor.fromRefObject / Anchor.fromFn
77
~positionMethod: PositionerSharedTypes.positionMethod=?,
88
~side: PositionerSharedTypes.side=?,
99
~sideOffset: PositionerSharedTypes.sideOffsetConfigSideOffset=?,
1010
~align: PositionerSharedTypes.align=?,
1111
~alignOffset: PositionerSharedTypes.sideOffsetConfigAlignOffset=?,
12-
~collisionBoundary: DistTypes.Boundary.t=?, // ⓘ was `Boundary` — opaque; build with Boundary.fromElement / Boundary.clippingAncestors / Boundary.fromElements / Boundary.fromType
12+
~collisionBoundary: DistTypes.Boundary.t=?, // ⓘ was `Boundary` — opaque; build with Boundary.fromElement / Boundary.clippingAncestors / Boundary.fromElements / Boundary.fromCollisionBoundaryConfig
1313
~collisionPadding: string=?, // ⚪ loose — was `Padding`
1414
~sticky: bool=?,
1515
~arrowPadding: float=?,

benchmark/baselines/_base-ui-components_react/bindings/DistTypes.res

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ type getBoundingClientRectConfig = {
1010
}
1111
module GetClientRectsTarget = {
1212
type t
13-
external fromTypes: array<getBoundingClientRectConfig> => t = "%identity"
13+
external fromGetBoundingClientRectConfigs: array<getBoundingClientRectConfig> => t = "%identity"
1414
external fromDOMRectList: WebTypes.domRectList => t = "%identity"
1515
}
1616
type virtualElement = {
@@ -36,13 +36,13 @@ module Anchor = {
3636
external fromElement: Dom.element => t = "%identity"
3737
external fromVirtualElement: virtualElement => t = "%identity"
3838
external fromRefObject: React.ref<Nullable.t<Dom.element>> => t = "%identity"
39-
external fromType: (unit => AnchorTarget.t) => t = "%identity"
39+
external fromFn: (unit => AnchorTarget.t) => t = "%identity"
4040
}
4141
module Boundary = {
4242
type t
4343
external fromElement: Dom.element => t = "%identity"
4444
external fromClippingAncestors: [#"clipping-ancestors"] => t = "%identity"
4545
let clippingAncestors: t = fromClippingAncestors(#"clipping-ancestors")
4646
external fromElements: array<Dom.element> => t = "%identity"
47-
external fromType: collisionBoundaryConfig => t = "%identity"
47+
external fromCollisionBoundaryConfig: collisionBoundaryConfig => t = "%identity"
4848
}

benchmark/baselines/_base-ui-components_react/bindings/MenuPositioner.res

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ external renderFn: ((PositionerSharedTypes.htmlProps, PositionerSharedTypes.menu
33

44
@module("@base-ui-components/react") @scope("ContextMenu") @react.component
55
external make: (
6-
~anchor: DistTypes.Anchor.t=?, // ⓘ was `Element | VirtualElement | RefObject<Element> | (() => Element | VirtualElement)` — opaque; build with Anchor.fromElement / Anchor.fromVirtualElement / Anchor.fromRefObject / Anchor.fromType
6+
~anchor: DistTypes.Anchor.t=?, // ⓘ was `Element | VirtualElement | RefObject<Element> | (() => Element | VirtualElement)` — opaque; build with Anchor.fromElement / Anchor.fromVirtualElement / Anchor.fromRefObject / Anchor.fromFn
77
~positionMethod: PositionerSharedTypes.positionMethod=?,
88
~side: PositionerSharedTypes.side=?,
99
~sideOffset: PositionerSharedTypes.sideOffsetConfigSideOffset=?,
1010
~align: PositionerSharedTypes.align=?,
1111
~alignOffset: PositionerSharedTypes.sideOffsetConfigAlignOffset=?,
12-
~collisionBoundary: DistTypes.Boundary.t=?, // ⓘ was `Boundary` — opaque; build with Boundary.fromElement / Boundary.clippingAncestors / Boundary.fromElements / Boundary.fromType
12+
~collisionBoundary: DistTypes.Boundary.t=?, // ⓘ was `Boundary` — opaque; build with Boundary.fromElement / Boundary.clippingAncestors / Boundary.fromElements / Boundary.fromCollisionBoundaryConfig
1313
~collisionPadding: string=?, // ⚪ loose — was `Padding`
1414
~sticky: bool=?,
1515
~arrowPadding: float=?,

benchmark/baselines/_base-ui-components_react/bindings/NavigationMenuPositioner.res

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ external renderFn: ((PositionerSharedTypes.htmlProps, PositionerSharedTypes.navi
33

44
@module("@base-ui-components/react") @scope("NavigationMenu") @react.component
55
external make: (
6-
~anchor: DistTypes.Anchor.t=?, // ⓘ was `Element | VirtualElement | RefObject<Element> | (() => Element | VirtualElement)` — opaque; build with Anchor.fromElement / Anchor.fromVirtualElement / Anchor.fromRefObject / Anchor.fromType
6+
~anchor: DistTypes.Anchor.t=?, // ⓘ was `Element | VirtualElement | RefObject<Element> | (() => Element | VirtualElement)` — opaque; build with Anchor.fromElement / Anchor.fromVirtualElement / Anchor.fromRefObject / Anchor.fromFn
77
~positionMethod: PositionerSharedTypes.positionMethod=?,
88
~side: PositionerSharedTypes.side=?,
99
~sideOffset: PositionerSharedTypes.sideOffsetConfigSideOffset=?,
1010
~align: PositionerSharedTypes.align=?,
1111
~alignOffset: PositionerSharedTypes.sideOffsetConfigAlignOffset=?,
12-
~collisionBoundary: DistTypes.Boundary.t=?, // ⓘ was `Boundary` — opaque; build with Boundary.fromElement / Boundary.clippingAncestors / Boundary.fromElements / Boundary.fromType
12+
~collisionBoundary: DistTypes.Boundary.t=?, // ⓘ was `Boundary` — opaque; build with Boundary.fromElement / Boundary.clippingAncestors / Boundary.fromElements / Boundary.fromCollisionBoundaryConfig
1313
~collisionPadding: string=?, // ⚪ loose — was `Padding`
1414
~sticky: bool=?,
1515
~arrowPadding: float=?,

benchmark/baselines/_base-ui-components_react/bindings/PopoverPositioner.res

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ external renderFn: ((PositionerSharedTypes.htmlProps, PositionerSharedTypes.popo
33

44
@module("@base-ui-components/react") @scope("Popover") @react.component
55
external make: (
6-
~anchor: DistTypes.Anchor.t=?, // ⓘ was `Element | VirtualElement | RefObject<Element> | (() => Element | VirtualElement)` — opaque; build with Anchor.fromElement / Anchor.fromVirtualElement / Anchor.fromRefObject / Anchor.fromType
6+
~anchor: DistTypes.Anchor.t=?, // ⓘ was `Element | VirtualElement | RefObject<Element> | (() => Element | VirtualElement)` — opaque; build with Anchor.fromElement / Anchor.fromVirtualElement / Anchor.fromRefObject / Anchor.fromFn
77
~positionMethod: PositionerSharedTypes.positionMethod=?,
88
~side: PositionerSharedTypes.side=?,
99
~sideOffset: PositionerSharedTypes.sideOffsetConfigSideOffset=?,
1010
~align: PositionerSharedTypes.align=?,
1111
~alignOffset: PositionerSharedTypes.sideOffsetConfigAlignOffset=?,
12-
~collisionBoundary: DistTypes.Boundary.t=?, // ⓘ was `Boundary` — opaque; build with Boundary.fromElement / Boundary.clippingAncestors / Boundary.fromElements / Boundary.fromType
12+
~collisionBoundary: DistTypes.Boundary.t=?, // ⓘ was `Boundary` — opaque; build with Boundary.fromElement / Boundary.clippingAncestors / Boundary.fromElements / Boundary.fromCollisionBoundaryConfig
1313
~collisionPadding: string=?, // ⚪ loose — was `Padding`
1414
~sticky: bool=?,
1515
~arrowPadding: float=?,

benchmark/baselines/_base-ui-components_react/bindings/PreviewCardPositioner.res

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ external renderFn: ((PositionerSharedTypes.htmlProps, PositionerSharedTypes.prev
33

44
@module("@base-ui-components/react") @scope("PreviewCard") @react.component
55
external make: (
6-
~anchor: DistTypes.Anchor.t=?, // ⓘ was `Element | VirtualElement | RefObject<Element> | (() => Element | VirtualElement)` — opaque; build with Anchor.fromElement / Anchor.fromVirtualElement / Anchor.fromRefObject / Anchor.fromType
6+
~anchor: DistTypes.Anchor.t=?, // ⓘ was `Element | VirtualElement | RefObject<Element> | (() => Element | VirtualElement)` — opaque; build with Anchor.fromElement / Anchor.fromVirtualElement / Anchor.fromRefObject / Anchor.fromFn
77
~positionMethod: PositionerSharedTypes.positionMethod=?,
88
~side: PositionerSharedTypes.side=?,
99
~sideOffset: PositionerSharedTypes.sideOffsetConfigSideOffset=?,
1010
~align: PositionerSharedTypes.align=?,
1111
~alignOffset: PositionerSharedTypes.sideOffsetConfigAlignOffset=?,
12-
~collisionBoundary: DistTypes.Boundary.t=?, // ⓘ was `Boundary` — opaque; build with Boundary.fromElement / Boundary.clippingAncestors / Boundary.fromElements / Boundary.fromType
12+
~collisionBoundary: DistTypes.Boundary.t=?, // ⓘ was `Boundary` — opaque; build with Boundary.fromElement / Boundary.clippingAncestors / Boundary.fromElements / Boundary.fromCollisionBoundaryConfig
1313
~collisionPadding: string=?, // ⚪ loose — was `Padding`
1414
~sticky: bool=?,
1515
~arrowPadding: float=?,

benchmark/baselines/_base-ui-components_react/bindings/SelectPositioner.res

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ external renderFn: ((PositionerSharedTypes.htmlProps, PositionerSharedTypes.sele
44
@module("@base-ui-components/react") @scope("Select") @react.component
55
external make: (
66
~alignItemWithTrigger: bool=?,
7-
~anchor: DistTypes.Anchor.t=?, // ⓘ was `Element | VirtualElement | RefObject<Element> | (() => Element | VirtualElement)` — opaque; build with Anchor.fromElement / Anchor.fromVirtualElement / Anchor.fromRefObject / Anchor.fromType
7+
~anchor: DistTypes.Anchor.t=?, // ⓘ was `Element | VirtualElement | RefObject<Element> | (() => Element | VirtualElement)` — opaque; build with Anchor.fromElement / Anchor.fromVirtualElement / Anchor.fromRefObject / Anchor.fromFn
88
~positionMethod: PositionerSharedTypes.positionMethod=?,
99
~side: PositionerSharedTypes.side=?,
1010
~sideOffset: PositionerSharedTypes.sideOffsetConfigSideOffset=?,
1111
~align: PositionerSharedTypes.align=?,
1212
~alignOffset: PositionerSharedTypes.sideOffsetConfigAlignOffset=?,
13-
~collisionBoundary: DistTypes.Boundary.t=?, // ⓘ was `Boundary` — opaque; build with Boundary.fromElement / Boundary.clippingAncestors / Boundary.fromElements / Boundary.fromType
13+
~collisionBoundary: DistTypes.Boundary.t=?, // ⓘ was `Boundary` — opaque; build with Boundary.fromElement / Boundary.clippingAncestors / Boundary.fromElements / Boundary.fromCollisionBoundaryConfig
1414
~collisionPadding: string=?, // ⚪ loose — was `Padding`
1515
~sticky: bool=?,
1616
~arrowPadding: float=?,

benchmark/baselines/_base-ui-components_react/bindings/ToastPositioner.res

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ external make: (
6262
~sideOffset: PositionerSharedTypes.sideOffsetConfigSideOffset=?,
6363
~align: PositionerSharedTypes.align=?,
6464
~alignOffset: PositionerSharedTypes.sideOffsetConfigAlignOffset=?,
65-
~collisionBoundary: DistTypes.Boundary.t=?, // ⓘ was `Boundary` — opaque; build with Boundary.fromElement / Boundary.clippingAncestors / Boundary.fromElements / Boundary.fromType
65+
~collisionBoundary: DistTypes.Boundary.t=?, // ⓘ was `Boundary` — opaque; build with Boundary.fromElement / Boundary.clippingAncestors / Boundary.fromElements / Boundary.fromCollisionBoundaryConfig
6666
~collisionPadding: string=?, // ⚪ loose — was `Padding`
6767
~arrowPadding: float=?,
6868
~disableAnchorTracking: bool=?,

0 commit comments

Comments
 (0)