Skip to content

fix: normalize v9 peer dependencies - #36559

Open
Martin Hochel (Hotell) wants to merge 7 commits into
microsoft:masterfrom
Hotell:fix/remove-unused-react-dom-peer-dependencies
Open

fix: normalize v9 peer dependencies#36559
Martin Hochel (Hotell) wants to merge 7 commits into
microsoft:masterfrom
Hotell:fix/remove-unused-react-dom-peer-dependencies

Conversation

@Hotell

@Hotell Martin Hochel (Hotell) commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Removes v9 peer dependencies we never use, and adds a generator that stops us re-introducing inconsistent ones.

Why

Peers we don't import aren't free. A package's peer set is part of its virtual instance key, so declaring react-dom in a package that never touches the DOM forks the virtual store on a dimension the package doesn't care about, and asks consumers to satisfy a module that is never loaded.

What changed

before after
react-dom peer (v9) 76 7
@types/react-dom peer (v9) 76 0
@types/react required peer 76 0 (now optional)

react-dom / @types/react-dom — only 7 packages reference react-dom in shipped code, verified against source and built lib/. The rest inherited the declaration from the scaffolding template. @types/react-dom is dropped everywhere; it appears in 0 of 337 api.md reports and 0 of 698 emitted .d.ts, so it was never part of the published type surface.

Removal is safe in the install models Fluent UI targets: hoisted layouts resolve react-dom from the consuming app, which always has it, and isolated layouts propagate the peer from the packages that genuinely declare it. Yarn PnP strict is explicitly not targeted — it has no ancestor walk, so it would require every package in the chain to re-declare peers it never imports. That is the trade-off this PR makes deliberately, and missing-peer-forward remains available (--checks=all) for anyone who needs to audit it.

@types/react — erased at build time, so only TypeScript consumers need it. Marked optional, which stops npm 7+ auto-installing React types into JS-only projects. Safe only because it's types-only: optional silences the install warning without making a module resolvable, so react and react-dom stay required.

use-sync-external-store — declared ^1.2.0 while promising react <20.0.0, but React 19 support landed in 1.4.0 and the lockfile was pinned to 1.2.2, so the combination we shipped was the broken one. Floor is now ^1.4.0.

eslint-plugin-react-components — declared eslint >= 8.0.0 and typescript >= 5.0.0, both wider than @typescript-eslint/utils supports, promising eslint 8.0–8.56 and typescript 6 support its own dependency can't honour.

Enforcement

New verify-peer-dependencies generator, run on PRs as --tag=vNext:

yarn nx g @fluentui/workspace-plugin:verify-peer-dependencies --tag=vNext

Default checks are installer-independent: incompatible-peer-range, invalid-peer-range, orphaned-peer-meta. All derived from the package.json graph, so results are deterministic. Only publishable packages are verified.

A fourth check, missing-peer-forward, is opt-in (--checks=all) because whether it matters depends on the installer:

Installer Peer resolution
npm / yarn classic (hoisted) require walks up the tree; nearest ancestor providing the peer wins
pnpm, midgard-yarn-strict (RFC 0042) peer metadata is propagated up the graph automatically, stopping at a real dependency or a local workspace
Yarn PnP strict no ancestor walk — every package in the chain must declare it

Only the last requires re-declaring a dependency's peers, so enforcing it by default would push us toward declaring peers we don't use — the opposite of this PR.

Notes for review

  • Metadata only. No source, build output or runtime behaviour changes.
  • react-positioning and react-storybook-addon-export-to-sandbox match react-dom only in a comment URL and in sandbox template strings, so they correctly get no peer.
  • react-conformance-griffel had the inverse bug — it declared @types/react-dom but not react-dom, while doing await import('react-dom') at runtime. Swapped.
  • Range containment is decided by probing range boundaries, not semver.subset, which rejects >=16.14.0 <20.0.0^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 even though the caret union is exactly >=16.8.0 <20.0.0.
  • Verified: yarn install --immutable exits 0, 285 workspace-plugin tests pass, --tag=vNext reports no violations.

Related Issue(s)

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown

📊 Bundle size report

Package & Exports Baseline (minified/GZIP) PR Change
react-charts
AreaChart
406.848 kB
127.167 kB
406.848 kB
127.165 kB

-2 B
react-charts
DeclarativeChart
758.392 kB
222.671 kB
758.392 kB
222.67 kB

-1 B
react-charts
DonutChart
317.679 kB
97.962 kB
317.679 kB
97.961 kB

-1 B
react-charts
FunnelChart
309.079 kB
94.761 kB
309.079 kB
94.759 kB

-2 B
react-charts
GanttChart
389.978 kB
121.648 kB
389.978 kB
121.647 kB

-1 B
react-charts
GaugeChart
317.029 kB
97.386 kB
317.029 kB
97.385 kB

-1 B
react-charts
HeatMapChart
392.118 kB
122.69 kB
392.118 kB
122.688 kB

-2 B
react-charts
HorizontalBarChart
297.355 kB
90.454 kB
297.355 kB
90.453 kB

-1 B
react-charts
Legends
236.542 kB
71.038 kB
236.542 kB
71.037 kB

-1 B
react-charts
LineChart
418.197 kB
130.296 kB
418.197 kB
130.295 kB

-1 B
react-charts
PolarChart
345.534 kB
107.992 kB
345.534 kB
107.991 kB

-1 B
react-charts
ScatterChart
397.521 kB
124.355 kB
397.521 kB
124.354 kB

-1 B
react-charts
VerticalBarChart
434.512 kB
129.325 kB
434.512 kB
129.324 kB

-1 B
react-charts
VerticalStackedBarChart
404.076 kB
125.831 kB
404.076 kB
125.83 kB

-1 B
react-components
react-components: Accordion, Button, FluentProvider, Image, Menu, Popover
227.126 kB
68.407 kB
227.126 kB
68.406 kB

-1 B
react-components
react-components: entire library
1.283 MB
322.083 kB
1.283 MB
322.082 kB

-1 B
react-headless-components-preview
react-headless-components-preview: entire library
238.015 kB
67.01 kB
238.015 kB
67.008 kB

-2 B
react-headless-components-preview
@fluentui/react-headless-components-preview/teaching-popover
36.041 kB
11.992 kB
36.041 kB
11.99 kB

-2 B
react-menu
Menu (including children components)
160.778 kB
51.107 kB
160.778 kB
51.105 kB

-2 B
react-menu
Menu (including selectable components)
163.944 kB
51.736 kB
163.944 kB
51.734 kB

-2 B
Unchanged fixtures
Package & Exports Size (minified/GZIP)
global-context
createContext
510 B
326 B
global-context
createContextSelector
531 B
334 B
keyboard-key
keyboard-key package
3.927 kB
1.99 kB
keyboard-keys
Multiple keyCodes
50 B
70 B
keyboard-keys
Multiple keys
87 B
94 B
keyboard-keys
Single key
44 B
64 B
keyboard-keys
Single keyCode
39 B
59 B
priority-overflow
createOverflowManager
5.372 kB
2.174 kB
react
ActivityItem
71.511 kB
23.466 kB
react
Announced
38.766 kB
13.382 kB
react
Autofill
15.557 kB
4.803 kB
react
Breadcrumb
200.779 kB
59.775 kB
react
Button
194.363 kB
56.054 kB
react
ButtonGrid
179.244 kB
54.067 kB
react
Calendar
121.293 kB
36.952 kB
react
Callout
84.456 kB
27.693 kB
react
Check
53.471 kB
17.936 kB
react
Checkbox
60.225 kB
19.994 kB
react
ChoiceGroup
65.717 kB
21.576 kB
react
ChoiceGroupOption
59.03 kB
19.464 kB
react
Coachmark
92.829 kB
29.434 kB
react
Color
7.94 kB
3.172 kB
react
ColorPicker
135.091 kB
42.254 kB
react
ComboBox
250.592 kB
71.678 kB
react
CommandBar
201.824 kB
59.54 kB
react
ContextualMenu
154.274 kB
47.7 kB
react
DatePicker
183.22 kB
56.023 kB
react
DateTimeUtilities
5.447 kB
1.917 kB
react
DetailsList
230.008 kB
66.014 kB
react
Dialog
210.074 kB
62.5 kB
react
Divider
19.893 kB
6.936 kB
react
DocumentCard
215.813 kB
63.838 kB
react
DragDrop
8.546 kB
2.789 kB
react
DraggableZone
34.582 kB
11.592 kB
react
Dropdown
233.079 kB
68.135 kB
react
ExtendedPicker
97.086 kB
27.95 kB
react
Fabric
42.006 kB
14.453 kB
react
Facepile
209.377 kB
62.564 kB
react
FloatingPicker
240.836 kB
68.395 kB
react
FocusTrapZone
17.113 kB
5.936 kB
react
FocusZone
55.391 kB
17.548 kB
react
Grid
179.244 kB
54.067 kB
react
GroupedList
135.213 kB
40.822 kB
react
GroupedListV2
122.837 kB
37.889 kB
react
HoverCard
96.917 kB
30.798 kB
react
Icon
52.159 kB
17.378 kB
react
Icons
66.627 kB
24.479 kB
react
Image
47.183 kB
15.787 kB
react
Keytip
81.486 kB
26.762 kB
react
KeytipData
14.201 kB
4.644 kB
react
KeytipLayer
103.226 kB
32.003 kB
react
Keytips
106 kB
33.025 kB
react
Label
38.627 kB
13.349 kB
react
Layer
48.346 kB
16.468 kB
react
Link
39.947 kB
13.766 kB
react
List
39.648 kB
12.531 kB
react
MarqueeSelection
74.764 kB
22.533 kB
react
MessageBar
189.392 kB
56.484 kB
react
Modal
93.845 kB
30.319 kB
react
Nav
186.842 kB
55.869 kB
react
OverflowSet
33.636 kB
11.36 kB
react
Overlay
41.175 kB
14.19 kB
react
Panel
200.3 kB
59.482 kB
react
Persona
114.726 kB
36.572 kB
react
PersonaCoin
114.726 kB
36.572 kB
react
PersonaPresence
58.341 kB
19.48 kB
react
Pickers
297.834 kB
83.212 kB
react
Pivot
187.719 kB
56.623 kB
react
Popup
12.427 kB
4.226 kB
react
Positioning
23.112 kB
7.761 kB
react
PositioningContainer
73.679 kB
23.818 kB
react
ProgressIndicator
39.765 kB
13.638 kB
react
Rating
82.323 kB
26.225 kB
react
Fluent UI React (entire library)
1.017 MB
283.569 kB
react
ResizeGroup
13.467 kB
4.42 kB
react
ResponsiveMode
8.271 kB
3.006 kB
react
ScrollablePane
55.802 kB
17.829 kB
react
SearchBox
187.632 kB
56.064 kB
react
SelectableOption
927 B
479 B
react
SelectedItemsList
231.319 kB
67.352 kB
react
Selection
42.719 kB
12.376 kB
react
Separator
35.667 kB
12.227 kB
react
Shimmer
49.51 kB
16.36 kB
react
ShimmeredDetailsList
240.74 kB
68.726 kB
react
Slider
57.89 kB
19.313 kB
react
SpinButton
191.276 kB
57.148 kB
react
Spinner
42.053 kB
14.565 kB
react
Stack
42.343 kB
14.467 kB
react
Sticky
32.868 kB
10.589 kB
react
Styling
46.377 kB
15.242 kB
react
SwatchColorPicker
189.597 kB
57.615 kB
react
TeachingBubble
204.608 kB
60.477 kB
react
Text
37.19 kB
12.879 kB
react
TextField
81.017 kB
25.418 kB
react
Theme
43.822 kB
14.276 kB
react
ThemeGenerator
12.525 kB
4.15 kB
react
TimePicker
240.406 kB
69.476 kB
react
Toggle
46.48 kB
16.079 kB
react
Tooltip
87.247 kB
28.262 kB
react
Utilities
83.123 kB
25.271 kB
react
Viewport
24.178 kB
7.716 kB
react
WeeklyDayPicker
101.544 kB
31.769 kB
react
WindowProvider
1.262 kB
614 B
react-accordion
Accordion (including children components)
91.918 kB
29.05 kB
react-aria
ARIA - useARIAButtonProps
1.35 kB
631 B
react-aria
ARIA - AriaLiveAnnouncer
3.265 kB
1.499 kB
react-avatar
Avatar
48.382 kB
15.299 kB
react-avatar
AvatarGroup
17.259 kB
6.885 kB
react-avatar
AvatarGroupItem
61.366 kB
19.157 kB
react-badge
Badge
24.116 kB
7.78 kB
react-badge
CounterBadge
24.838 kB
8.039 kB
react-badge
PresenceBadge
24.084 kB
8.712 kB
react-breadcrumb
@fluentui/react-breadcrumb - package
103.659 kB
29.076 kB
react-button
Button
33.813 kB
8.929 kB
react-button
CompoundButton
40.693 kB
10.24 kB
react-button
MenuButton
38.759 kB
10.311 kB
react-button
SplitButton
48.177 kB
12.046 kB
react-button
ToggleButton
53.469 kB
11.065 kB
react-calendar-compat
Calendar Compat
143.32 kB
40.566 kB
react-card
Card - All
94.094 kB
27.347 kB
react-card
Card
86.69 kB
25.464 kB
react-card
CardFooter
12.653 kB
5.024 kB
react-card
CardHeader
15.2 kB
5.872 kB
react-card
CardPreview
12.73 kB
5.137 kB
react-charting
AreaChart
305.154 kB
95.909 kB
react-charting
ChartHoverCard
37.29 kB
12.738 kB
react-charting
DeclarativeChart
680.459 kB
193.864 kB
react-charting
DonutChart
204.416 kB
64.319 kB
react-charting
GanttChart
287.222 kB
90.82 kB
react-charting
GaugeChart
197.731 kB
61.769 kB
react-charting
GroupedVerticalBarChart
297.023 kB
93.155 kB
react-charting
HeatMapChart
287.919 kB
90.559 kB
react-charting
HorizontalBarChart
127.475 kB
40.118 kB
react-charting
HorizontalBarChartWithAxis
298.395 kB
93.219 kB
react-charting
Legends
152.037 kB
46.832 kB
react-charting
LineChart
334.72 kB
102.945 kB
react-charting
MultiStackedBarChart
182.57 kB
55.844 kB
react-charting
PieChart
135.059 kB
42.757 kB
react-charting
PolarChart
235.701 kB
74.718 kB
react-charting
SankeyChart
158.576 kB
49.642 kB
react-charting
ScatterChart
291.196 kB
92.181 kB
react-charting
Sparkline
88.144 kB
29.862 kB
react-charting
StackedBarChart
176.145 kB
53.411 kB
react-charting
TreeChart
85.764 kB
27.102 kB
react-charting
VerticalBarChart
308.085 kB
95.291 kB
react-charting
VerticalStackedBarChart
305.195 kB
94.973 kB
react-charts
GroupedVerticalBarChart
398.061 kB
124.439 kB
react-charts
HorizontalBarChartWithAxis
63 B
83 B
react-charts
SankeyChart
213.787 kB
68.717 kB
react-charts
Sparkline
80.888 kB
26.864 kB
react-checkbox
Checkbox
30.771 kB
10.912 kB
react-color-picker
ColorArea
31.022 kB
11.702 kB
react-color-picker
ColorPicker
16.039 kB
6.434 kB
react-color-picker
ColorSlider
26.053 kB
10.219 kB
react-combobox
Combobox (including child components)
139.308 kB
45.062 kB
react-combobox
Dropdown (including child components)
139.073 kB
44.814 kB
react-components
react-components: Button, FluentProvider & webLightTheme
67.461 kB
19.461 kB
react-components
react-components: FluentProvider & webLightTheme
40.684 kB
13.549 kB
react-datepicker-compat
DatePicker Compat
217.437 kB
64.403 kB
react-dialog
Dialog (including children components)
90.974 kB
28.25 kB
react-divider
Divider
16.233 kB
5.843 kB
react-field
Field
22.301 kB
8.308 kB
react-headless-components-preview
@fluentui/react-headless-components-preview/tag-picker
53.982 kB
17.74 kB
react-image
Image
13.487 kB
5.444 kB
react-input
Input
26.23 kB
8.639 kB
react-jsx-runtime
Classic Pragma
1.282 kB
613 B
react-jsx-runtime
JSX Dev Runtime
2.137 kB
988 B
react-jsx-runtime
JSX Runtime
2.134 kB
948 B
react-label
Label
12.833 kB
5.166 kB
react-link
Link
16.397 kB
6.46 kB
react-list
List
75.535 kB
23.403 kB
react-list
ListItem
99.05 kB
30.27 kB
react-message-bar
MessageBar (all components)
25.283 kB
8.941 kB
react-motion
@fluentui/react-motion - createMotionComponent()
4.275 kB
1.864 kB
react-motion
@fluentui/react-motion - createPresenceComponent()
6.007 kB
2.484 kB
react-motion
@fluentui/react-motion - PresenceGroup
1.715 kB
816 B
react-overflow
hooks only
10.159 kB
3.679 kB
react-persona
Persona
55.335 kB
17.235 kB
react-popover
Popover
126.59 kB
40.869 kB
react-portal
Portal
13.844 kB
5.392 kB
react-portal-compat
PortalCompatProvider
5.341 kB
2.146 kB
react-positioning
usePositioning
28.985 kB
10.198 kB
react-positioning
useSafeZoneArea
13.398 kB
5.365 kB
react-progress
ProgressBar
20.207 kB
7.832 kB
react-provider
FluentProvider
20.076 kB
7.697 kB
react-radio
Radio
28.156 kB
9.153 kB
react-radio
RadioGroup
13.921 kB
5.609 kB
react-select
Select
26.099 kB
9.399 kB
react-slider
Slider
33.395 kB
11.506 kB
react-spinbutton
SpinButton
33.651 kB
11.009 kB
react-spinner
Spinner
23.593 kB
7.782 kB
react-swatch-picker
@fluentui/react-swatch-picker - package
93.176 kB
27.605 kB
react-switch
Switch
33.392 kB
10.562 kB
react-table
DataGrid
147.799 kB
43.973 kB
react-table
Table (Primitives only)
38.032 kB
12.685 kB
react-table
Table as DataGrid
119.67 kB
33.75 kB
react-table
Table (Selection only)
66.547 kB
18.953 kB
react-table
Table (Sort only)
65.21 kB
18.575 kB
react-tag-picker
@fluentui/react-tag-picker - package
174.753 kB
54.556 kB
react-tags
InteractionTag
13.619 kB
5.381 kB
react-tags
Tag
29.532 kB
9.334 kB
react-tags
TagGroup
70.796 kB
21.806 kB
react-teaching-popover
TeachingPopover
101.77 kB
32.204 kB
react-text
Text - Default
15.174 kB
5.877 kB
react-text
Text - Wrappers
18.333 kB
6.192 kB
react-textarea
Textarea
24.596 kB
8.908 kB
react-theme
Single theme token import
69 B
89 B
react-theme
Teams: all themes
37.967 kB
7.989 kB
react-theme
Teams: Light theme
20.797 kB
5.863 kB
react-timepicker-compat
TimePicker
142.027 kB
46.435 kB
react-toast
Toast (including Toaster)
91.478 kB
28.537 kB
react-tooltip
Tooltip
54.69 kB
19.443 kB
react-tree
FlatTree
136.683 kB
40.816 kB
react-tree
PersonaFlatTree
138.546 kB
41.356 kB
react-tree
PersonaTree
134.627 kB
40.138 kB
react-tree
Tree
132.77 kB
39.634 kB
react-utilities
SSRProvider
182 B
160 B
🤖 This report was generated against 3685a680cd5b0331decc14729c062172948b64a9

@@ -0,0 +1,7 @@
{

@github-actions github-actions Bot Aug 12, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🕵🏾‍♀️ visual changes to review in the Visual Change Report

vr-tests-react-components/Menu Converged - submenuIndicator slotted content 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Menu Converged - submenuIndicator slotted content.default - RTL.submenus open.chromium.png 404 Changed
vr-tests-react-components/Menu Converged - submenuIndicator slotted content.default.submenus open.chromium.png 413 Changed
vr-tests-react-components/Positioning 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Positioning.Positioning end.chromium.png 745 Changed
vr-tests-react-components/Positioning.Positioning end.updated 2 times.chromium.png 512 Changed
vr-tests-react-components/ProgressBar converged 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - High Contrast.default.chromium.png 181 Changed
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness.default.chromium.png 40 Changed
vr-tests-react-components/TagPicker 3 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/TagPicker.disabled - Dark Mode.disabled input hover.chromium.png 658 Changed
vr-tests-react-components/TagPicker.disabled - RTL.chromium.png 635 Changed
vr-tests-react-components/TagPicker.disabled.disabled input hover.chromium.png 677 Changed
vr-tests-web-components/Accordion 1 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-web-components/Accordion. - Dark Mode.normal.chromium_1.png 3398 Changed
vr-tests-web-components/Badge 1 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-web-components/Badge. - Dark Mode.normal.chromium.png 443 Changed
vr-tests-web-components/MenuList 1 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-web-components/MenuList. - RTL.2nd selected.chromium.png 17 Changed
vr-tests/Callout 3 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests/Callout.Bottom right edge - RTL.default.chromium.png 1124 Changed
vr-tests/Callout.Left bottom edge.default.chromium.png 3182 Changed
vr-tests/Callout.Top left edge.default.chromium.png 2212 Changed
vr-tests/Facepile 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests/Facepile.Add face.default.chromium.png 579 Changed
vr-tests/Facepile.Custom button styles.default.chromium.png 541 Changed
vr-tests/FocusTrapZones 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests/FocusTrapZones.Dialog nested in Panel - RTL.click on panel close button.chromium.png 12773 Changed
vr-tests/FocusTrapZones.Dialog nested in Panel.default.chromium.png 12877 Changed
vr-tests/Keytip 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests/Keytip.Disabled.default.chromium.png 26 Changed
vr-tests/Keytip.Root.default.chromium.png 55 Changed
vr-tests/react-charting-AreaChart 1 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests/react-charting-AreaChart.Custom Accessibility.default.chromium.png 11 Changed
vr-tests/react-charting-LineChart 3 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests/react-charting-LineChart.Multiple - RTL.default.chromium.png 200 Changed
vr-tests/react-charting-LineChart.Multiple - Dark Mode.default.chromium.png 181 Changed
vr-tests/react-charting-LineChart.Events.default.chromium.png 17 Changed
vr-tests/react-charting-MultiStackBarChart 3 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests/react-charting-MultiStackBarChart.Basic_Absolute - Dark Mode.default.chromium.png 363 Changed
vr-tests/react-charting-MultiStackBarChart.Basic_Absolute - RTL.default.chromium.png 343 Changed
vr-tests/react-charting-MultiStackBarChart.Basic_PartToWhole.default.chromium.png 359 Changed
vr-tests/react-charting-VerticalBarChart 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests/react-charting-VerticalBarChart.Basic - Secondary Y Axis.default.chromium.png 3 Changed
vr-tests/react-charting-VerticalBarChart.Grouped - Wrap Labels.default.chromium.png 3105 Changed

There were 6 duplicate changes discarded. Check the build logs for more information.

@Hotell
Martin Hochel (Hotell) force-pushed the fix/remove-unused-react-dom-peer-dependencies branch from 095103a to ff2f405 Compare August 18, 2026 14:25
@github-actions

Copy link
Copy Markdown

Pull request demo site: URL

@github-actions github-actions Bot added the CI label Aug 18, 2026
@Hotell
Martin Hochel (Hotell) force-pushed the fix/remove-unused-react-dom-peer-dependencies branch from 879408f to 80df2f2 Compare August 18, 2026 16:56
@Hotell Martin Hochel (Hotell) changed the title fix: remove unused react-dom peerDependencies from react-components packages fix: make v9 peer dependencies accurate and enforce peer chains Aug 18, 2026
@Hotell
Martin Hochel (Hotell) force-pushed the fix/remove-unused-react-dom-peer-dependencies branch 2 times, most recently from 7078329 to fe1e1de Compare August 19, 2026 11:12
@Hotell Martin Hochel (Hotell) changed the title fix: make v9 peer dependencies accurate and enforce peer chains fix: remove unused v9 peer dependencies and verify peer contracts Aug 19, 2026
@Hotell
Martin Hochel (Hotell) force-pushed the fix/remove-unused-react-dom-peer-dependencies branch 4 times, most recently from 6fb3147 to b7bf4e5 Compare August 19, 2026 15:17
@Hotell
Martin Hochel (Hotell) requested a balanced review from Copilot August 20, 2026 12:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Removes unnecessary v9 peers and adds automated peer-contract validation.

Changes:

  • Prunes react-dom/@types/react-dom peers and makes @types/react optional.
  • Adds and tests the peer-dependency verifier.
  • Updates dependency ranges, CI enforcement, lockfile, templates, and change files.

Reviewed changes

Copilot reviewed 168 out of 169 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
.github/workflows/pr.yml Runs peer verification in PR CI.
yarn.lock Synchronizes peer metadata and resolved dependencies.
tools/workspace-plugin/generators.json Registers the verifier generator.
tools/workspace-plugin/src/types.ts Types optional peer metadata.
tools/workspace-plugin/src/generators/verify-peer-dependencies/README.md Documents verifier behavior and usage.
tools/workspace-plugin/src/generators/verify-peer-dependencies/index.ts Implements peer-contract validation and fixes.
tools/workspace-plugin/src/generators/verify-peer-dependencies/index.spec.ts Tests verifier behavior.
tools/workspace-plugin/src/generators/verify-peer-dependencies/schema.json Defines the generator CLI schema.
tools/workspace-plugin/src/generators/verify-peer-dependencies/schema.ts Defines generator option types.
tools/workspace-plugin/src/generators/react-library/files/package.json__tmpl__ Updates generated peer defaults.
packages/react-components/component-selector-preview/library/package.json Prunes unused DOM peers.
packages/react-components/deprecated/react-alert/package.json Updates React peer metadata.
packages/react-components/deprecated/react-infobutton/package.json Updates React peer metadata.
packages/react-components/deprecated/react-virtualizer/package.json Retains runtime DOM peer and updates type metadata.
packages/react-components/eslint-plugin-react-components/package.json Narrows tooling peer ranges.
packages/react-components/global-context/package.json Makes React types optional.
packages/react-components/react-accordion/library/package.json Updates React peer metadata.
packages/react-components/react-aria/library/package.json Updates React peer metadata.
packages/react-components/react-avatar/library/package.json Updates React peer metadata.
packages/react-components/react-badge/library/package.json Updates React peer metadata.
packages/react-components/react-breadcrumb/library/package.json Updates React peer metadata.
packages/react-components/react-button/library/package.json Updates React peer metadata.
packages/react-components/react-calendar-compat/library/package.json Updates React peer metadata.
packages/react-components/react-card/library/package.json Updates React peer metadata.
packages/react-components/react-carousel/library/package.json Updates React peer metadata.
packages/react-components/react-checkbox/library/package.json Updates React peer metadata.
packages/react-components/react-color-picker/library/package.json Updates React peer metadata.
packages/react-components/react-colorpicker-compat/package.json Prunes unused DOM peers.
packages/react-components/react-combobox/library/package.json Retains runtime DOM peer and updates type metadata.
packages/react-components/react-components/package.json Prunes aggregate-package DOM peers.
packages/react-components/react-conformance-griffel/package.json Adds its runtime react-dom peer.
packages/react-components/react-context-selector/package.json Updates React peer metadata.
packages/react-components/react-datepicker-compat/library/package.json Updates React peer metadata.
packages/react-components/react-dialog/library/package.json Updates React peer metadata.
packages/react-components/react-divider/library/package.json Updates React peer metadata.
packages/react-components/react-drawer/library/package.json Updates React peer metadata.
packages/react-components/react-field/library/package.json Updates React peer metadata.
packages/react-components/react-headless-components-preview/library/package.json Updates React peer metadata.
packages/react-components/react-icons-compat/library/package.json Updates React peer metadata.
packages/react-components/react-image/library/package.json Updates React peer metadata.
packages/react-components/react-infolabel/library/package.json Updates React peer metadata.
packages/react-components/react-input/library/package.json Updates React peer metadata.
packages/react-components/react-jsx-runtime/package.json Makes React types optional.
packages/react-components/react-label/library/package.json Updates React peer metadata.
packages/react-components/react-link/library/package.json Updates React peer metadata.
packages/react-components/react-list/library/package.json Updates React peer metadata.
packages/react-components/react-menu-grid-preview/library/package.json Updates React peer metadata.
packages/react-components/react-menu/library/package.json Updates React peer metadata.
packages/react-components/react-message-bar/library/package.json Updates React peer metadata.
packages/react-components/react-migration-v0-v9/library/package.json Updates React peer metadata.
packages/react-components/react-migration-v8-v9/library/package.json Updates React peer metadata.
packages/react-components/react-motion-components-preview/library/package.json Updates React peer metadata.
packages/react-components/react-motion/library/package.json Updates React peer metadata.
packages/react-components/react-nav/library/package.json Updates React peer metadata.
packages/react-components/react-overflow/library/package.json Updates React peer metadata.
packages/react-components/react-persona/library/package.json Updates React peer metadata.
packages/react-components/react-popover/library/package.json Updates React peer metadata.
packages/react-components/react-portal-compat-context/package.json Makes React types optional.
packages/react-components/react-portal-compat/package.json Makes React types optional.
packages/react-components/react-portal/library/package.json Retains runtime DOM peer and updates type metadata.
packages/react-components/react-positioning/library/package.json Updates peers and React 19 shim support.
packages/react-components/react-progress/library/package.json Updates React peer metadata.
packages/react-components/react-provider/library/package.json Updates React peer metadata.
packages/react-components/react-radio/library/package.json Updates React peer metadata.
packages/react-components/react-rating/library/package.json Updates React peer metadata.
packages/react-components/react-search/library/package.json Updates React peer metadata.
packages/react-components/react-select/library/package.json Updates React peer metadata.
packages/react-components/react-shared-contexts/library/package.json Makes React types optional.
packages/react-components/react-skeleton/library/package.json Updates React peer metadata.
packages/react-components/react-slider/library/package.json Updates React peer metadata.
packages/react-components/react-spinbutton/library/package.json Updates React peer metadata.
packages/react-components/react-spinner/library/package.json Updates React peer metadata.
packages/react-components/react-storybook-addon/package.json Updates React peer metadata.
packages/react-components/react-swatch-picker/library/package.json Updates React peer metadata.
packages/react-components/react-switch/library/package.json Updates React peer metadata.
packages/react-components/react-table/library/package.json Updates React peer metadata.
packages/react-components/react-tabs/library/package.json Updates React peer metadata.
packages/react-components/react-tabster/package.json Updates React peer metadata.
packages/react-components/react-tag-picker/library/package.json Retains runtime DOM peer and updates type metadata.
packages/react-components/react-tags/library/package.json Updates React peer metadata.
packages/react-components/react-teaching-popover/library/package.json Updates peers and React 19 shim support.
packages/react-components/react-text/library/package.json Updates React peer metadata.
packages/react-components/react-textarea/library/package.json Updates React peer metadata.
packages/react-components/react-timepicker-compat/library/package.json Updates React peer metadata.
packages/react-components/react-toast/library/package.json Updates React peer metadata.
packages/react-components/react-toolbar/library/package.json Updates React peer metadata.
packages/react-components/react-tooltip/library/package.json Updates React peer metadata.
packages/react-components/react-tree/library/package.json Retains runtime DOM peer and updates type metadata.
packages/react-components/react-utilities-compat/library/package.json Prunes unused DOM peers.
packages/react-components/react-utilities/package.json Makes React types optional.
packages/react-components/recipes/package.json Prunes unused DOM peers.
packages/react-components/theme-designer/package.json Removes unused React DOM types.
change/@fluentui-eslint-plugin-react-components-bc46323e-82ab-4878-998b-5dd19fa5f0d9.json Records tooling peer-range changes.
change/@fluentui-global-context-7a997068-d2cd-40e5-bc0e-fe1c1c49dac8.json Records global-context metadata changes.
change/@fluentui-react-accordion-3ff07725-b0de-40fd-a3c0-03f8888556a5.json Records package metadata changes.
change/@fluentui-react-alert-c334ef5b-3340-456e-9b20-01518f659b0d.json Records package metadata changes.
change/@fluentui-react-aria-46908b84-678b-4100-bbda-a2746a4e245b.json Records package metadata changes.
change/@fluentui-react-avatar-d51a264b-1cbd-4f2d-8003-4a4a5bb8b3df.json Records package metadata changes.
change/@fluentui-react-badge-ca23953a-a40c-44ff-887c-0c9cff112211.json Records package metadata changes.
change/@fluentui-react-breadcrumb-e405c510-0fc5-48b6-b885-8dc9d8180580.json Records package metadata changes.
change/@fluentui-react-button-ce466318-9c22-47eb-bf13-4f3a2f31fadb.json Records package metadata changes.
change/@fluentui-react-calendar-compat-ccbf7c15-84c9-4b0f-8efc-6e7fa748c1e8.json Records package metadata changes.
change/@fluentui-react-card-88431a56-c924-462a-bbec-aefa053dced8.json Records package metadata changes.
change/@fluentui-react-carousel-4cf526f8-7a78-4be2-ab93-64b9286345d5.json Records package metadata changes.
change/@fluentui-react-checkbox-b7a78cf5-b865-4683-b144-7cc13d1ab386.json Records package metadata changes.
change/@fluentui-react-color-picker-366022a2-cc45-49f5-b386-90a0ca7985a6.json Records package metadata changes.
change/@fluentui-react-combobox-69be7590-10a9-47d5-8390-a8a9b65a28dc.json Records package metadata changes.
change/@fluentui-react-components-c784fdce-0d58-458e-b1dc-5fe22cb0b73a.json Records aggregate-package metadata changes.
change/@fluentui-react-conformance-griffel-38db03f4-bf4d-4f96-aff7-15a08a9c54ff.json Records corrected runtime peer.
change/@fluentui-react-context-selector-078ddf89-1482-4df3-9d02-c5a7298f91fd.json Records package metadata changes.
change/@fluentui-react-datepicker-compat-e36857fc-25fa-443a-9aec-4c41609b46af.json Records package metadata changes.
change/@fluentui-react-dialog-1dd8d5d1-b227-41e2-a6b5-8737120b6b33.json Records package metadata changes.
change/@fluentui-react-divider-4fc13923-6ea1-4d06-b7aa-66b049b36170.json Records package metadata changes.
change/@fluentui-react-drawer-314fddc3-532d-4707-a65a-6532c7721d59.json Records package metadata changes.
change/@fluentui-react-field-2523d3e4-82e7-42e6-ae9c-b68efbb42626.json Records package metadata changes.
change/@fluentui-react-headless-components-preview-e5d562de-3d30-4e15-922f-e81cae9a596b.json Records package metadata changes.
change/@fluentui-react-icons-compat-daf9887e-90ce-4592-bc2a-6aae3697d3d1.json Records package metadata changes.
change/@fluentui-react-image-630316f7-ca60-4df7-8180-6f4937686471.json Records package metadata changes.
change/@fluentui-react-infobutton-eb9ddf83-dc46-427d-af2e-e127c4467d7d.json Records package metadata changes.
change/@fluentui-react-infolabel-ce184655-6e81-476a-a9f9-be2bcf9c6035.json Records package metadata changes.
change/@fluentui-react-input-58a45833-5a26-4dee-a996-a87263ce8b3d.json Records package metadata changes.
change/@fluentui-react-jsx-runtime-52085c6f-b83c-4653-a8f6-20ac8c91db18.json Records optional type peer.
change/@fluentui-react-label-1b43f503-c66e-468d-adbd-76f3a3c245a4.json Records package metadata changes.
change/@fluentui-react-link-ee550c4d-6833-4673-9f64-9b617fbea249.json Records package metadata changes.
change/@fluentui-react-list-e8b5f1e8-b6ef-4f94-888c-969d88cf26e1.json Records package metadata changes.
change/@fluentui-react-menu-a71d8733-d2b2-45c4-8d52-c31430d8f337.json Records package metadata changes.
change/@fluentui-react-menu-grid-preview-834999cd-0767-47b9-9e71-e73617fe3719.json Records package metadata changes.
change/@fluentui-react-message-bar-fb5cfe30-3120-4734-a72d-0abc58f12ef7.json Records package metadata changes.
change/@fluentui-react-migration-v0-v9-89851263-3e34-4a40-8c47-f45cc4c36057.json Records package metadata changes.
change/@fluentui-react-migration-v8-v9-c6ef9430-cae4-4c7f-9e2a-946d50c4a88b.json Records package metadata changes.
change/@fluentui-react-motion-aa4a3ccb-9b7e-4715-8017-31482f566cc1.json Records package metadata changes.
change/@fluentui-react-motion-components-preview-d9979335-86c4-4403-9bd7-23af3384ce63.json Records package metadata changes.
change/@fluentui-react-nav-71a8cfb1-fa18-46c8-964e-7e7281f11af3.json Records package metadata changes.
change/@fluentui-react-overflow-2382dd3a-946b-4392-a56f-4569fe7726d9.json Records package metadata changes.
change/@fluentui-react-persona-472e5b4d-d4f8-4fe4-8c38-80ac99fdff3f.json Records package metadata changes.
change/@fluentui-react-popover-122a1340-9508-44e8-8d25-a715770abd23.json Records package metadata changes.
change/@fluentui-react-portal-588ea710-2e69-4705-8651-2d4b5dda1caa.json Records package metadata changes.
change/@fluentui-react-portal-compat-bce75f2c-7faa-4471-998d-9f85934372a5.json Records optional type peer.
change/@fluentui-react-portal-compat-context-a62389b7-9981-41b2-afa8-e5dcde741353.json Records optional type peer.
change/@fluentui-react-positioning-b9b4dd75-457b-4aea-966d-bbcf6a5245f6.json Records peer and shim updates.
change/@fluentui-react-progress-a1a624fe-a4f6-47a5-86ad-1107e48f5ce5.json Records package metadata changes.
change/@fluentui-react-provider-f3b506e3-3f11-4631-8e0d-88ab6b0c77f9.json Records package metadata changes.
change/@fluentui-react-radio-114fbcf1-b0e1-412c-9b04-57e6b85247d6.json Records package metadata changes.
change/@fluentui-react-rating-da5b2fde-4ff2-4385-8dba-89e74da8a996.json Records package metadata changes.
change/@fluentui-react-search-4fbf0876-311c-4bc8-8e7e-d7adc93e8bf0.json Records package metadata changes.
change/@fluentui-react-select-b7c3dbad-b6ad-4eba-9a96-b8c791725828.json Records package metadata changes.
change/@fluentui-react-shared-contexts-95aa313c-6bb2-4f9e-9ba3-a2df3faaba3e.json Records optional type peer.
change/@fluentui-react-skeleton-10f28d03-dee9-487c-8dbf-d627461b197c.json Records package metadata changes.
change/@fluentui-react-slider-586ccb38-8fec-468e-a814-a303e7686bb2.json Records package metadata changes.
change/@fluentui-react-spinbutton-6b3ce138-040a-47f7-813c-b71a31f6fe3f.json Records package metadata changes.
change/@fluentui-react-spinner-7729a37d-5349-42ca-be6c-af1f8b013490.json Records package metadata changes.
change/@fluentui-react-storybook-addon-5a140b97-f264-4192-ac32-d88f616d79e1.json Records package metadata changes.
change/@fluentui-react-swatch-picker-b955caa8-53ae-466d-93da-52246e107fa5.json Records package metadata changes.
change/@fluentui-react-switch-5ae26c41-a156-428a-b245-162495f9f31f.json Records package metadata changes.
change/@fluentui-react-table-46094913-ec55-4985-8a4f-1136d7b29f77.json Records package metadata changes.
change/@fluentui-react-tabs-0aa07b00-91d5-4f6d-9d78-3b233b30dd5f.json Records package metadata changes.
change/@fluentui-react-tabster-684d953d-5fd6-47df-bcf7-6beffc58604a.json Records package metadata changes.
change/@fluentui-react-tag-picker-c7f186a6-ef65-487f-8110-d8dc73aaeb5e.json Records package metadata changes.
change/@fluentui-react-tags-09d81c82-e6dc-40d7-86a0-cd246bb2808a.json Records package metadata changes.
change/@fluentui-react-teaching-popover-0d4ced75-add8-49dc-a00d-d630a03bebb6.json Records peer and shim updates.
change/@fluentui-react-text-5230b567-1687-438b-9098-72265f16df66.json Records package metadata changes.
change/@fluentui-react-textarea-1ce1688e-982d-44d3-a824-32d74ddf59c2.json Records package metadata changes.
change/@fluentui-react-timepicker-compat-dec37e01-a46e-4670-9581-069a85c300d5.json Records package metadata changes.
change/@fluentui-react-toast-3935b55c-5193-42fd-83aa-00573ec5e77e.json Records package metadata changes.
change/@fluentui-react-toolbar-14684d1b-d5e7-4cd6-bbe0-8a603d78d947.json Records package metadata changes.
change/@fluentui-react-tooltip-50be0942-91c8-4df6-b83c-134dcd067bd7.json Records package metadata changes.
change/@fluentui-react-tree-9bdd7730-b0e4-4a28-9827-142279a42978.json Records package metadata changes.
change/@fluentui-react-utilities-d4dd17bf-1221-4061-a710-804f121d4e05.json Records optional type peer.
change/@fluentui-react-virtualizer-c60d58e2-39b7-4ccd-bc2c-ce543bc738fc.json Records package metadata changes.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread tools/workspace-plugin/src/generators/verify-peer-dependencies/index.ts Outdated
Comment thread tools/workspace-plugin/src/generators/verify-peer-dependencies/index.ts Outdated
Comment thread packages/react-components/eslint-plugin-react-components/package.json Outdated
Comment thread packages/react-components/react-components/package.json
Martin Hochel (Hotell) and others added 7 commits August 20, 2026 18:01
Verifies that the peer dependency promises we publish are internally consistent
and satisfiable. All checks are derived from the package.json graph, so results
are deterministic.

- incompatible-peer-range  a declared range accepts versions the dependency does
                           not support
- invalid-peer-range       peer range is not a valid semver range
- orphaned-peer-meta       peerDependenciesMeta entry with no peerDependencies
- missing-peer-forward     a dependency requires a non-optional peer this package
                           neither declares nor owns (opt-in, see below)

`missing-peer-forward` is off by default because whether it matters depends
entirely on the installer. Hoisted layouts (npm, yarn classic) resolve a peer by
walking up the directory tree to the nearest ancestor that provides it. Isolated
layouts (pnpm, midgard-yarn-strict / npm RFC 0042) additionally propagate peer
metadata up the graph themselves, stopping at a real dependency or a local
workspace. In both, re-declaring a dependency's peer is a no-op. Only Yarn PnP
strict has no ancestor walk and requires every package in the chain to declare
it. Since a package's peer set is part of its virtual instance key, declaring
peers it never imports actively forks the virtual store and asks consumers to
satisfy a module that is never loaded.

Only publishable packages are verified: a private package is never installed by
a consumer, so it carries no peer contract. It is still read, because publishable
packages depend on it. Scoping is available by `project`, nx `tag` and `checks`;
an unknown value throws rather than silently verifying nothing.

Range containment is decided by probing the boundary versions of both ranges
rather than by `semver.subset`, which tests containment against each comparator
set of the dominant range in isolation and so rejects a range spanning several
carets even when their union covers it:

    subset('>=16.14.0 <20.0.0', '>=16.8.0 <20.0.0')                         -> true
    subset('>=16.14.0 <20.0.0', '^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0') -> false

Both describe the same set, and the second form is how the react ecosystem
usually expresses support.

`--fix` runs to a fixpoint because forwarding cascades, and never throws:
throwing from a generator makes nx discard the whole tree, which would silently
drop the fixes just applied.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
76 packages under packages/react-components declared `react-dom` and/or
`@types/react-dom` as peers. Only 7 reference `react-dom` in shipped code,
verified against source and built `lib/` output. The rest inherited the
declaration from the scaffolding template.

A package's peer set is part of its virtual instance key, so declaring a peer it
never imports forks the virtual store on a dimension the package does not care
about, and asks consumers who never render to the DOM to satisfy a module that is
never loaded. Removing them is safe in every install model: hoisted layouts
resolve `react-dom` from the consuming app, which always has it, and isolated
layouts propagate the peer from the packages that genuinely declare it.

`@types/react-dom` is dropped everywhere except `react-migration-v8-v9`, which
depends on `@fluentui/react` (v8) and inherits that peer from the v8 contract. It
appears in 0 of 337 checked-in api.md reports and 0 of 698 emitted .d.ts files,
so it was never part of the published type surface.

`@fluentui/react-conformance-griffel` had the inverse problem: it declared
`@types/react-dom` but not `react-dom`, while doing `await import('react-dom')`
and `await import('react-dom/client')` at runtime. Those are now swapped.

The react-library generator template is updated to match, so newly scaffolded
packages no longer start with peers they do not use.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
react-positioning and react-teaching-popover declared `use-sync-external-store`
as `^1.2.0` while promising react `>=16.14.0 <20.0.0`. React 19 support was only
added to that package in 1.4.0, so the declared floor did not match the react
support contract - the caret happened to allow a working version, but nothing
guaranteed it and the lockfile was pinned to 1.2.2, i.e. the combination we
shipped was the broken one.

The floor is now `^1.4.0`, the first release whose peer range includes `^19.0.0`.
Lower-end support is unaffected: 1.4.0 still accepts react 16.8+.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
`@types/react` and `@types/react-dom` are erased at build time, so a consumer
only needs them to type-check - a plain JS app never does. Declaring them as
required peers made npm 7+ auto-install react types into projects that have no
use for them, and made resolvers warn about a package that is never loaded at
runtime.

They are marked optional on all 76 publishable packages/react-components
packages. The react-library generator template matches, so newly scaffolded
packages do not reintroduce the required declaration.

This is safe only because they are types-only. `optional` silences the install
warning without making the module resolvable, so for a peer that is actually
imported it would convert a loud install-time warning into a silent runtime
crash. `react` and `react-dom` therefore stay required.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…-components

The package declared `eslint: >= 8.0.0` and `typescript: >= 5.0.0`, both wider
than `@typescript-eslint/utils` supports, so it promised support its own
dependency cannot honour. On eslint 8.0.0 the failure is immediate:

    TypeError: Class extends value undefined is not a constructor or null
      at @typescript-eslint/utils/dist/ts-eslint/eslint/FlatESLint.js:12
         class FlatESLint extends use_at_your_own_risk_1.FlatESLint

`FlatESLint` was added to eslint's `use-at-your-own-risk` entry point in 8.57.0,
which is exactly why typescript-eslint pins that floor.

The eslint range is set to `^8.57.0 || ^9.0.0` rather than also allowing
`^10.0.0`. eslint 10 support is only present from `@typescript-eslint/utils`
8.56.0, while this package declares `^8.46.2` - a consumer lockfile may legally
resolve 8.46.2 and would then receive an unsatisfiable contract. Advertising
eslint 10 requires raising that dependency floor first.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Runs the installer-independent checks - incompatible ranges, invalid ranges and
orphaned peer metadata - so a package cannot promise support its dependencies
cannot honour. `missing-peer-forward` stays off by default, since it only applies
under Yarn PnP strict.

Scoped to `--tag=vNext`, which is green as of the preceding commits. v8 and the
docsite still carry incompatible ranges and stay out of scope, so the gate holds
from the start instead of being introduced red.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Generated from the final state of the branch rather than incrementally, so each
file describes what actually ships for that package. The earlier files had gone
stale: they still described the scheduler forwarding and the react-dom
forwarding that were reverted after the 1JS feedback, and one covered a package
that is no longer modified at all.

77 packages, matching `yarn check:change` exactly. `none` is used for the four
packages whose beachball config disallows patch.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Hotell
Martin Hochel (Hotell) force-pushed the fix/remove-unused-react-dom-peer-dependencies branch from b7bf4e5 to 48c8b4f Compare August 20, 2026 16:03
@Hotell

Copy link
Copy Markdown
Contributor Author

Thanks — all five were valid and are now addressed. Each was verified empirically rather than taken at face value.

1. --fix was a silent no-op. Correct, and a regression from making missing-peer-forward opt-in: applyFixes only repairs that kind, so a bare --fix collected nothing. --fix now implies it, and the 🛠️ FIX hint is no longer printed for kinds --fix cannot repair.

2. Boundary probe missed ranges unbounded below. Confirmed — isSubset('*', '>=16.14.0') and isSubset('<20.0.0', '>=16.14.0 <20.0.0') both returned true. Probes are now seeded with semver.minVersion(sub). This also surfaced that * was being skipped by the non-semver escape hatch; * is valid semver and a real (maximally broad) claim, so it is now validated. Regression tests added for both.

3. Optional peers suppressed range validation. Correct — optional means the peer may be absent, not that its version constraint disappears. Now only the missing-forward branch is skipped; incompatible-peer-range still runs when the peer is declared.

4. ESLint 10 over-advertised. Confirmed, and the sharpest catch. Verified the concrete failure on eslint 8.0.0:

TypeError: Class extends value undefined is not a constructor or null
  at @typescript-eslint/utils/dist/ts-eslint/eslint/FlatESLint.js:12

FlatESLint landed in eslint 8.57.0's use-at-your-own-risk, which is exactly why typescript-eslint pins that floor. Went with dropping ^10.0.0^8.57.0 || ^9.0.0, matching our declared ^8.46.2 floor; raising the floor to ^8.56.0 is the alternative if eslint 10 support is wanted later.

This also exposed a real limitation: the checker reads the installed manifest (8.57.1), not the declared floor (8.46.2) — which is why it missed this. Added an opt-in unverified-peer-range check that detects the gap offline. Scanning all 13 external deps with peers, only 2 had floor ≠ resolved peers — @typescript-eslint/utils and use-sync-external-store — i.e. precisely the two bugs this PR was already about.

5. Strict-PnP inconsistency. Fair — the body claimed universal safety while the README documented PnP strict needing forwarding. Made the position explicit rather than implicit: Fluent UI targets hoisted and isolated installs, PnP strict is deliberately not targeted, and missing-peer-forward stays available via --checks=all for anyone auditing it.

Also corrected the reporter wording, which said X requires ... when it meant X@<installed version> requires ....

297 tests pass, yarn install --immutable exits 0, and the vNext gate is still green.

@Hotell Martin Hochel (Hotell) changed the title fix: remove unused v9 peer dependencies and verify peer contracts fix: normalize v9 peer dependencies Aug 21, 2026
@Hotell
Martin Hochel (Hotell) marked this pull request as ready for review August 21, 2026 09:15
@Hotell
Martin Hochel (Hotell) requested a review from a team as a code owner August 21, 2026 09:15
@PaulGMardling

Copy link
Copy Markdown
Contributor

Is the missing version check on dependencies[peer] vs requiredRange intentional (npm/yarn's job at install time) or worth adding here too?

@PaulGMardling

Copy link
Copy Markdown
Contributor

Was keeping react-dom on theme-designer deliberate since it's private, or should it follow the same rule as react-positioning?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants