Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
f297ab5
feat(eslint-rules): add consistent-base-hook rule
Hotell May 21, 2026
2ca0c42
chore(eslint-plugin): enable consistent-base-hook for v9 sources
Hotell May 21, 2026
2c2c0e9
chore(react-components): suppress consistent-base-hook violations in …
Hotell May 21, 2026
cbbd8c5
feat(eslint-rules): allow useFocusWithin by default in consistent-bas…
Hotell May 21, 2026
a8ea39b
chore(react-components): drop now-unneeded consistent-base-hook suppr…
Hotell May 21, 2026
153a636
feat(eslint-rules): allow useFocusVisible by default in consistent-ba…
Hotell May 21, 2026
3504c3a
feat(eslint-rules): allow keyborg-only react-tabster APIs and drop ke…
Hotell May 21, 2026
578c7da
chore(react-tooltip): drop now-unneeded consistent-base-hook suppress…
Hotell May 21, 2026
338183a
feat(eslint-rules): auto-detect forbidden runtime deps via TS Program…
Hotell May 25, 2026
b4ed759
feat(eslint-rules): add allowTypeImports option to consistent-base-hook
Hotell May 25, 2026
77938a0
style: encapsulate import tracking and apply explicit type imports/ig…
Hotell May 25, 2026
6ada519
fix(eslint-rules): validate Ref import origin in consistent-base-hook…
Hotell May 25, 2026
dffca5a
revert(eslint-rules): undo unrelated import change in consistent-call…
Hotell May 26, 2026
1084d65
feat(eslint-rules): detect type-leakage through watched packages in c…
Hotell May 26, 2026
f441aaf
feat(eslint-rules): extend consistent-base-hook with paired state hoo…
Hotell May 26, 2026
4dd78a0
refactor(eslint-rules): split consistent-base-hook into base-hook-sig…
Hotell May 26, 2026
07a9b99
refactor(eslint-rules): inline runtime helpers into base-hook-no-forb…
Hotell May 26, 2026
c7614a7
refactor(eslint-rules): inline base-hook-detector into each rule, dro…
Hotell May 26, 2026
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
2 changes: 2 additions & 0 deletions packages/eslint-plugin/src/internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ const __internal = {
/** @type {import('eslint').Linter.RulesRecord} */
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/Charts-DonutChart 1 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Charts-DonutChart.Dynamic.default.chromium.png 5581 Changed
vr-tests-react-components/Menu Converged - submenuIndicator slotted content 1 screenshots
Image Name Diff(in Pixels) Image Type
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 629 Changed
vr-tests-react-components/Positioning.Positioning end.updated 2 times.chromium.png 742 Changed
vr-tests-react-components/ProgressBar converged 3 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - Dark Mode.default.chromium.png 34 Changed
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness.default.chromium.png 41 Changed
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - High Contrast.default.chromium.png 34 Changed
vr-tests-react-components/TagPicker 1 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/TagPicker.disabled - High Contrast.chromium.png 1319 Changed
vr-tests-web-components/Avatar 1 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-web-components/Avatar. - Dark Mode.normal.chromium.png 10380 Changed
vr-tests-web-components/RadioGroup 1 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-web-components/RadioGroup. - Dark Mode.1st selected.chromium_2.png 119 Changed
vr-tests/Callout 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests/Callout.No callout width specified.default.chromium.png 2143 Changed
vr-tests/Callout.Root.default.chromium.png 2195 Changed
vr-tests/react-charting-LineChart 1 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests/react-charting-LineChart.Multiple - RTL.default.chromium.png 200 Changed

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

rules: {
'@nx/workspace-consistent-callback-type': 'error',
'@nx/workspace-base-hook-signature': 'error',
'@nx/workspace-base-hook-no-forbidden-runtime': 'error',
'@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 @@ -27,6 +27,7 @@ type UseTagGroupBaseOptions = {
* @param props - props from this instance of TagGroup (without appearance, size)
* @param ref - reference to root HTMLDivElement of TagGroup
*/
// eslint-disable-next-line @nx/workspace-base-hook-signature -- accepts an extra `options` arg used internally by `useTagGroup_unstable` to coordinate focus after a tag is dismissed
export const useTagGroupBase_unstable = (
props: TagGroupBaseProps,
ref: React.Ref<HTMLDivElement>,
Expand Down
7 changes: 7 additions & 0 deletions tools/eslint-rules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import {
RULE_NAME as consistentCallbackTypeName,
rule as consistentCallbackType,
} from './rules/consistent-callback-type';
import { RULE_NAME as baseHookSignatureName, rule as baseHookSignature } from './rules/base-hook-signature';
import {
RULE_NAME as baseHookNoForbiddenRuntimeName,
rule as baseHookNoForbiddenRuntime,
} from './rules/base-hook-no-forbidden-runtime';

/**
* Import your custom workspace rules at the top of this file.
Expand Down Expand Up @@ -32,6 +37,8 @@ module.exports = {
*/
rules: {
[consistentCallbackTypeName]: consistentCallbackType,
[baseHookSignatureName]: baseHookSignature,
[baseHookNoForbiddenRuntimeName]: baseHookNoForbiddenRuntime,
[noRestrictedGlobalsName]: noRestrictedGlobals,
[noMissingJsxPragmaName]: noMissingJsxPragma,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Placeholder so the tsconfig has an actual file to anchor the project.
// RuleTester test cases reference filenames inside this directory but their
// content comes from the inline `code` field.
export const dummy = 1;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Placeholder anchor for typed RuleTester cases. The actual code being linted
// comes from each test's inline `code` field; this file just needs to exist so
// that the fixture tsconfig's Program contains the filename used by tests.
export {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { useB } from './b';

export function useA(): number {
return useB() + 1;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { useA } from './a';

export function useB(): number {
// Pretend lazy ref to break true cycle at runtime; the static graph is cyclic.
return (useA as unknown as () => number).length;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Cyclic re-export — exercises the cycle-safety of the transitive walk.
export { useA } from './a';
export { useB } from './b';
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export function runHeavy(): { tag: 'heavy' } {
return { tag: 'heavy' };
}

export type HeavyOptions = { kind: 'heavy' };
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function runLight(_opts?: { mode: 'light' }): void {
/* noop */
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { runHeavy } from 'heavy-runtime';

export type HeavyType = { tag: 'heavy' };

export function useHeavy(): HeavyType {
return runHeavy();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { HeavyType } from './heavy';

export { useHeavy } from './heavy';
export { useLight } from './light';
export type { LightOptions } from './light';
// Re-export of a type-only thing from the heavy module — must not count as a runtime reach.
export type { HeavyType } from './heavy';

export type HeavyWrapper = { tag: 'heavy-wrapper'; inner: HeavyType };
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { runLight } from 'light-helper';

export type LightOptions = { mode: 'light' };

export function useLight(opts?: LightOptions): void {
runLight(opts);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "node",
"lib": ["ES2022", "DOM"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"noEmit": true,
"baseUrl": ".",
"paths": {
"watched-pkg": ["./stubs/watched-pkg/index.ts"],
"watched-pkg/*": ["./stubs/watched-pkg/*"],
"heavy-runtime": ["./stubs/heavy-runtime/index.ts"],
"light-helper": ["./stubs/light-helper/index.ts"],
"cyclic-pkg": ["./stubs/cyclic-pkg/index.ts"]
}
},
"include": ["src/**/*.ts", "src/**/*.tsx", "stubs/**/*.ts"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const useSiblingBase_unstable = (props: { a: number }, ref: React.Ref<HTMLElement>) => {
return { props, ref };
};
Loading
Loading