Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
Comment thread
Hotell marked this conversation as resolved.
"type": "none",
"comment": "chore: forbid react-icons, react-motion and Griffel in v9 base hooks",
"packageName": "@fluentui/react-avatar",
"email": "martinhochel@microsoft.com",
"dependentChangeType": "none"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "none",
"comment": "chore: forbid react-icons, react-motion and Griffel in v9 base hooks",
"packageName": "@fluentui/react-combobox",
"email": "martinhochel@microsoft.com",
"dependentChangeType": "none"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "none",
"comment": "chore: forbid react-icons, react-motion and Griffel in v9 base hooks",
"packageName": "@fluentui/react-headless-components-preview",
"email": "martinhochel@microsoft.com",
"dependentChangeType": "none"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "none",
"comment": "chore: forbid react-icons, react-motion and Griffel in v9 base hooks",
"packageName": "@fluentui/react-menu",
"email": "martinhochel@microsoft.com",
"dependentChangeType": "none"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "none",
"comment": "chore: forbid react-icons, react-motion and Griffel in v9 base hooks",
"packageName": "@fluentui/react-message-bar",
"email": "martinhochel@microsoft.com",
"dependentChangeType": "none"
}
22 changes: 21 additions & 1 deletion packages/eslint-plugin/src/internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,26 @@ function shouldRegisterInternal() {

const shouldRegister = shouldRegisterInternal();

/**
* Runtime packages that must not be reachable from v9 base hooks (`use<Name>Base_unstable`).
*
* Configured explicitly instead of relying on the rule default so the boundary enforced at lint
* time stays in sync with, and visible next to, the `forbiddenPackages` asserted at build time by
* `bundle-isolation.config.json`.
*
* Names are matched exactly, so a package and its satellites have to be listed separately —
* `@fluentui/react-motion-components-preview` is not covered by `@fluentui/react-motion`, and the
* `@griffel/*` glob the bundle config uses has to be spelled out here.
*/
const baseHookForbiddenRuntimes = [
'tabster',
'@fluentui/react-icons',
'@fluentui/react-motion',
'@fluentui/react-motion-components-preview',
'@griffel/react',
'@griffel/core',
];

/**
*
* this will be removed after https://github.com/microsoft/fluentui/issues/30332
Expand All @@ -36,7 +56,7 @@ const __internal = {
rules: {
'@nx/workspace-consistent-callback-type': 'error',
'@nx/workspace-base-hook-signature': 'error',
'@nx/workspace-base-hook-no-forbidden-runtime': 'error',
'@nx/workspace-base-hook-no-forbidden-runtime': ['error', { forbiddenRuntimes: baseHookForbiddenRuntimes }],
'@nx/workspace-no-restricted-globals': restrictedGlobals.react,
'@nx/workspace-no-missing-jsx-pragma': ['error', { runtime: 'automatic' }],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,14 @@ export const useAvatarGroupPopover_unstable = (props: AvatarGroupPopoverProps):
* @param props - AvatarGroupPopover props
* @returns AvatarGroupPopover state
*/
export const useAvatarGroupPopoverBase_unstable = (props: AvatarGroupPopoverBaseProps): AvatarGroupPopoverBaseState => {
export const useAvatarGroupPopoverBase_unstable = (
// Tracked debt: unlike the other base types these subtract only `size`, so the `root` slot keeps
// `PopoverProps` — and with it `surfaceMotion` — in the base API surface. Type-only, so nothing
// is emitted; clearing it means dropping the motion slot from the base props/state.
// eslint-disable-next-line @nx/workspace-base-hook-no-forbidden-runtime
props: AvatarGroupPopoverBaseProps,
// eslint-disable-next-line @nx/workspace-base-hook-no-forbidden-runtime
): AvatarGroupPopoverBaseState => {
const layout = useAvatarGroupContext_unstable(ctx => ctx.layout);
const { indicator = 'count', count = React.Children.count(props.children), children, ...restOfProps } = props;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export const useComboboxBase_unstable = (

const triggerRef = React.useRef<HTMLInputElement>(null);

// eslint-disable-next-line @nx/workspace-base-hook-no-forbidden-runtime -- tracked debt: styled slot pulls Griffel
const listbox = useListboxSlot(props.listbox, useMergedRefs(comboboxPopupRef, activeDescendantListboxRef), {
state: comboboxInternalState,
triggerRef,
Expand Down Expand Up @@ -97,6 +98,7 @@ export const useComboboxBase_unstable = (

const showClearIcon = selectedOptions.length > 0 && !disabled && clearable && !multiselect;
const state: BaseComboboxState = {
// eslint-disable-next-line @nx/workspace-base-hook-no-forbidden-runtime -- tracked debt: styled slot pulls Griffel
components: { root: 'div', input: 'input', expandIcon: 'span', listbox: Listbox, clearIcon: 'span' },
root: rootSlot,
input: triggerSlot,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export const useDropdownBase_unstable = (
const [comboboxPopupRef, comboboxTargetRef] = useComboboxPositioning(props);

const triggerRef = React.useRef<HTMLButtonElement>(null);
// eslint-disable-next-line @nx/workspace-base-hook-no-forbidden-runtime -- tracked debt: styled slot pulls Griffel
const listbox = useListboxSlot(props.listbox, useMergedRefs(comboboxPopupRef, activeDescendantListboxRef), {
state: dropdownInternalState,
triggerRef,
Expand Down Expand Up @@ -98,6 +99,7 @@ export const useDropdownBase_unstable = (

const showClearButton = selectedOptions.length > 0 && !disabled && clearable && !multiselect;
const state: DropdownBaseState = {
// eslint-disable-next-line @nx/workspace-base-hook-no-forbidden-runtime -- tracked debt: styled slot pulls Griffel
components: { root: 'div', button: 'button', clearButton: 'button', expandIcon: 'span', listbox: Listbox },
root: rootSlot,
button: trigger,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
"$schema": "../../../../tools/verify-bundle-isolation/schema.json",
"fixturesRoot": "./bundle-size",
"externals": ["react", "react-dom", "react/jsx-runtime", "react/compiler-runtime"],
"forbiddenPackages": ["tabster", "@griffel/*", "@fluentui/react-icons"],
"forbiddenPackages": [
"tabster",
"@griffel/*",
"@fluentui/react-icons",
"@fluentui/react-motion",
"@fluentui/react-motion-components-preview"
],
"allowedViolations": {}
Comment thread
Hotell marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ export const useMenuBase_unstable = (
mouseCoordinates: { x: 0, y: 0 },
});

// eslint-disable-next-line @nx/workspace-base-hook-no-forbidden-runtime -- tracked debt: SafeZoneArea pulls Griffel
const safeZoneHandle = useSafeZoneArea({
disabled: !enableSafeZone,
timeout: typeof safeZone === 'object' ? safeZone.timeout : 300,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "cyclic-heavy-pkg",
"version": "1.0.0",
"main": "index.ts"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "heavy-runtime",
"version": "1.0.0",
"main": "index.ts"
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,16 @@ export type CleanTag = { tag: 'clean' };
export function useHeavy(): { tag: 'heavy' } {
return runHeavy();
}

// Mirrors the v9 shape: a styled props bag carrying a forbidden-runtime slot, and the base bag
// derived from it by subtracting exactly that member.
export type StyledProps = { tag: 'styled'; motion?: HeavyOptions; label: string };

export type DerivedBaseProps = Omit<StyledProps, 'motion'>;

// Only coupling is the constraint of a type parameter — the parameter itself is declared here,
// not in the forbidden runtime, so the reach is invisible unless constraints are followed.
export type HeavyConstrainedCallback = <T extends HeavyOptions>(value: T) => void;

// Same shape, but with a constraint that stays inside this package.
export type CleanConstrainedCallback = <T extends CleanTag>(value: T) => void;
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ export type { HeavyType } from './heavy';
export type { CleanTag } from './heavy';

export type HeavyWrapper = { tag: 'heavy-wrapper'; inner: HeavyType };
export type { StyledProps } from './heavy';
export type { DerivedBaseProps } from './heavy';
export type { HeavyConstrainedCallback } from './heavy';
export type { CleanConstrainedCallback } from './heavy';
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Pure re-export of a workspace forbidden runtime. `getAliasedSymbol` collapses the whole chain
// in one hop, so the intermediate `workspace-runtime` specifier is never visited and ownership
// has to be recovered from the leaf declaration's own package manifest.
export { runWorkspaceHeavy } from 'workspace-runtime';
export type { WorkspaceHeavyOptions } from 'workspace-runtime';
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// A forbidden runtime that ships as a *workspace* package: it is path-mapped straight to source,
// so its files carry no `node_modules` segment to read a package name from.
export function runWorkspaceHeavy(): { tag: 'workspace-heavy' } {
return { tag: 'workspace-heavy' };
}

export type WorkspaceHeavyOptions = { kind: 'workspace-heavy' };
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "workspace-runtime",
"version": "1.0.0",
"main": "index.ts"
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
"relay-pkg": ["./stubs/relay-pkg/index.ts"],
"component-pkg": ["./stubs/component-pkg/index.ts"],
"typed-dist-pkg": ["./stubs/typed-dist-pkg/index.d.ts"],
"workspace-runtime": ["./stubs/workspace-runtime/index.ts"],
"workspace-relay-pkg": ["./stubs/workspace-relay-pkg/index.ts"],
"heavy-runtime": ["./stubs/heavy-runtime/index.ts"],
"heavy-runtime/*": ["./stubs/heavy-runtime/*"],
"light-helper": ["./stubs/light-helper/index.ts"],
Expand Down
Loading
Loading