Skip to content

Commit 9cf4444

Browse files
Saadnajmiclaude
andauthored
chore(lint): migrate repo to oxlint and stabilize validation (#4075)
* chore(lint): replace eslint with oxlint tooling and config Swap ESLint for oxlint across the monorepo. This includes deleting all per-package eslint.config.js files, adding oxlint.config.ts where needed, updating package.json scripts/dependencies, renaming eslint-config-rules to lint-config-rules, fixing depcheck dynamic dependency injection, and updating dependency profiles and align-deps presets. No behavioral code changes — only tooling and configuration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(lint): apply mechanical oxlint auto-fixes Automated and trivial fixes flagged by oxlint rules: - @ts-ignore@ts-expect-error - export * → explicit named exports - forEach → for-of loops - Array<T> → T[] syntax - Missing React key props - Unused parameter underscoring - Removed stale eslint-disable/ts-ignore comments - Minor code simplifications (spread removal, type assertion fix) No behavioral changes — all transformations are semantically equivalent. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(lint): suppress exhaustive-deps for intentional mount-only hooks Add eslint-disable-next-line react-hooks/exhaustive-deps comments to hooks that intentionally use empty or partial dependency arrays by design (e.g., mount-only effects, stable ref callbacks). These suppressions preserve the existing intended behavior while satisfying oxlint's stricter linting. No code logic changes — only lint suppression comments added/reworded. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(hooks): restructure hook dependencies and fix stale closures Correctness fixes for React hook dependency arrays flagged by oxlint's exhaustive-deps rule. Key changes: - RadioGroup: destructure context, use functional state updaters (fixes state mutation bug where .push() didn't trigger re-render) - ContextualMenu/Submenu: destructure callbacks, add missing deps - Menu: fix comma expression bug in MenuPopover, fix self-referential useMemo in useMenu.android, destructure context - Shimmer/Spinner/ActivityIndicator: useRef for Animated.Value (prevents recreation on re-render), correct animation effect deps - Drawer: add isFirstOpen dep, destructure callbacks - useAsPressable/useAsToggle: add missing callback deps (stale closures) - TabListAnimatedIndicator: useMemo → useRef for stable animation values - TabList: refactor disabled tab search loop - useSlot/useMergeRefs: correct dependency arrays - Chip/Checkbox/OverflowItem: narrow or correct dep arrays Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(changeset): Migrate from ESLint to oxlint Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(hooks): address reviewer feedback on useMergeRefs, useSlot, and bundle script - useMergeRefs: restore [...refs] spread so React compares individual ref values rather than the always-new rest parameter array instance - useSlot: re-add `component` to useMemo deps to ensure memo invalidates when the component prop changes - fluent-tester: revert bundle script to original `rnx-cli bundle --dev false` which is more flexible than explicit per-platform chaining Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(ContextualMenu): restore onDismiss in Callout slot props Destructuring onDismiss from userProps removed it from the ...rest spread that gets passed to the Callout root slot. The Callout needs onDismiss to function correctly — without it, the MenuButton SPACE key test consistently failed because the menu couldn't dismiss/toggle properly. Fix: destructure onDismiss AND explicitly pass it back in the root slot props. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 6585876 commit 9cf4444

269 files changed

Lines changed: 1281 additions & 1576 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/strict-sides-hang.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
---
2+
"@uifabricshared/foundation-composable": patch
3+
"@fluentui-react-native/experimental-appearance-additions": patch
4+
"@uifabricshared/theming-react-native": patch
5+
"@uifabricshared/foundation-settings": patch
6+
"@fluentui-react-native/experimental-activity-indicator": patch
7+
"@fluentui-react-native/experimental-native-font-metrics": patch
8+
"@uifabricshared/foundation-compose": patch
9+
"@fluentui-react-native/experimental-native-date-picker": patch
10+
"@uifabricshared/foundation-tokens": patch
11+
"@fluentui-react-native/themed-stylesheet": patch
12+
"@uifabricshared/themed-settings": patch
13+
"@fluentui-react-native/contextual-menu": patch
14+
"@fluentui-react-native/lint-config-rules": patch
15+
"@uifabricshared/theme-registry": patch
16+
"@fluentui-react-native/vibrancy-view": patch
17+
"@fluentui-react-native/focus-trap-zone": patch
18+
"@fluentui-react-native/notification": patch
19+
"@uifabricshared/theming-ramp": patch
20+
"@fluentui-react-native/experimental-menu-button": patch
21+
"@fluentui-react-native/interactive-hooks": patch
22+
"@fluentui-react-native/persona-coin": patch
23+
"@fluentui-react-native/menu-button": patch
24+
"@fluentui-react-native/radio-group": patch
25+
"@fluentui-react-native/experimental-checkbox": patch
26+
"@fluentui-react-native/dropdown": patch
27+
"@fluentui-react-native/experimental-expander": patch
28+
"@fluentui-react-native/overflow": patch
29+
"@fluentui-react-native/composition": patch
30+
"@fluentui-react-native/use-styling": patch
31+
"@fluentui-react-native/android-theme": patch
32+
"@fluentui-react-native/default-theme": patch
33+
"@fluentui-react-native/theming-utils": patch
34+
"@fluentui-react-native/focus-zone": patch
35+
"@fluentui-react-native/pressable": patch
36+
"@fluentui-react-native/separator": patch
37+
"@fluentui-react-native/popover": patch
38+
"@fluentui-react-native/experimental-shimmer": patch
39+
"@fluentui-react-native/spinner": patch
40+
"@fluentui-react-native/tooltip": patch
41+
"@fluentui-react-native/use-tokens": patch
42+
"@fluentui-react-native/theme-tokens": patch
43+
"@fluentui-react-native/checkbox": patch
44+
"@fluentui-react-native/experimental-avatar": patch
45+
"@fluentui-react-native/drawer": patch
46+
"@fluentui-react-native/experimental-shadow": patch
47+
"@fluentui-react-native/framework": patch
48+
"@fluentui-react-native/use-slots": patch
49+
"@fluentui-react-native/apple-theme": patch
50+
"@fluentui-react-native/theme-types": patch
51+
"@fluentui-react-native/win32-theme": patch
52+
"@fluentui-react-native/callout": patch
53+
"@fluentui-react-native/divider": patch
54+
"@fluentui-react-native/persona": patch
55+
"@fluentui-react-native/tablist": patch
56+
"@fluentui-react-native/use-slot": patch
57+
"@fluentui-react-native/avatar": patch
58+
"@fluentui-react-native/button": patch
59+
"@fluentui-react-native/switch": patch
60+
"@fluentui-react-native/badge": patch
61+
"@fluentui-react-native/input": patch
62+
"@fluentui-react-native/stack": patch
63+
"@fluentui-react-native/chip": patch
64+
"@fluentui-react-native/icon": patch
65+
"@fluentui-react-native/link": patch
66+
"@fluentui-react-native/menu": patch
67+
"@fluentui-react-native/text": patch
68+
"@fluentui-react-native/theme": patch
69+
"@fluentui-react-native/framework-base": patch
70+
"@fluentui/react-native": patch
71+
"@fluentui-react-native/adapters": patch
72+
"@fluentui-react-native/styling-utils": patch
73+
"@fluentui-react-native/tokens": patch
74+
"@fluentui-react-native/tester": patch
75+
"@fluentui-react-native/codemods": patch
76+
---
77+
78+
Migrate from ESLint to oxlint

.vscode/settings.json

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44
"editor.trimAutoWhitespace": true,
55
"editor.insertSpaces": true,
66
"editor.tabSize": 2,
7-
"editor.codeActionsOnSave": {
8-
"source.fixAll.eslint": "explicit"
9-
},
107
// Use the OXC formatter as the default formatter which matches what yarn format will do
118
"editor.defaultFormatter": "oxc.oxc-vscode",
129

@@ -16,9 +13,6 @@
1613
"prettier.printWidth": 140,
1714
"prettier.trailingComma": "all",
1815

19-
"eslint.enable": true,
20-
"eslint.workingDirectories": [{ "pattern": "./packages/framework/eslint-config-rules" }], // use the common eslint config file
21-
2216
"explorer.fileNesting.enabled": true,
2317
"explorer.fileNesting.patterns": {
2418
"*.js": "${capture}.js.map, ${capture}.d.ts, ${capture}.d.ts.map",

CLAUDE.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,10 @@ The project uses **Yarn 4** (Berry) in **pnpm mode** with **Lage** as the task r
5656
```bash
5757
yarn build # TypeScript build for all packages (outputs to lib/ and lib-commonjs/)
5858
yarn test # Build, lint, and run tests across all packages
59-
yarn lint # ESLint across all packages
60-
yarn bundle # Bundle all packages
61-
yarn buildci # Full CI pipeline: build + test + lint + bundle + depcheck + check-publishing
59+
yarn lint # Lint across all packages
60+
yarn lage bundle # Bundle all packages
61+
yarn bundle:repo # Convenience wrapper for the repo bundle graph
62+
yarn lage buildci # Configured CI graph alias from lage.config.mjs
6263
yarn clean # Clean build artifacts
6364
```
6465

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,9 @@ For running tasks the repo has switched to using [Lage](https://github.com/micro
102102

103103
- `yarn build` - does the typescript build for all packages in the repository
104104
- `yarn test` - will build, lint, and run any applicable tests on all packages in the repo
105-
- `yarn bundle` - will bundle all packages in the repo
106-
- `yarn buildci` - will build, lint, run tests, and bundle everything in the repo
105+
- `yarn lage bundle` - will bundle all packages in the repo
106+
- `yarn bundle:repo` - convenience wrapper for `yarn lage bundle`
107+
- `yarn lage buildci` - will build, lint, run tests, and run the configured repo checks
107108

108109
Note that Lage uses caching to avoid redundant steps and has very minimal output. To avoid caching add `--no-cache` as a command line argument. Similarly adding `--verbose` will give more detailed output.
109110

apps/E2E/eslint.config.js

Lines changed: 0 additions & 11 deletions
This file was deleted.

apps/E2E/oxlint.config.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import baseConfig from '@fluentui-react-native/lint-config-rules';
2+
import { defineConfig } from 'oxlint';
3+
4+
export default defineConfig({
5+
extends: [baseConfig],
6+
rules: {
7+
'@rnx-kit/no-export-all': 'off',
8+
'typescript/class-literal-property-style': 'off',
9+
'typescript/no-duplicate-enum-values': 'off',
10+
'typescript/no-invalid-void-type': 'off',
11+
},
12+
});

apps/E2E/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"e2etest:macos": "cross-env APPIUM_HOME=.appium wdio run wdio.conf.macos.js",
3939
"e2etest:win32": "cross-env APPIUM_HOME=.appium wdio run wdio.conf.win32.js",
4040
"e2etest:windows": "rimraf errorShots reports && cross-env APPIUM_HOME=.appium wdio run wdio.conf.windows.js",
41-
"lint": "fluentui-scripts eslint",
41+
"lint": "fluentui-scripts lint",
4242
"lint-package": "fluentui-scripts lint-package"
4343
},
4444
"dependencies": {
@@ -47,9 +47,9 @@
4747
"devDependencies": {
4848
"@babel/core": "catalog:",
4949
"@babel/runtime": "catalog:",
50-
"@fluentui-react-native/eslint-config-rules": "workspace:*",
5150
"@fluentui-react-native/focus-zone": "workspace:*",
5251
"@fluentui-react-native/kit-config": "workspace:*",
52+
"@fluentui-react-native/lint-config-rules": "workspace:*",
5353
"@fluentui-react-native/scripts": "workspace:*",
5454
"@office-iss/react-native-win32": "^0.81.0",
5555
"@react-native/metro-babel-transformer": "^0.81.0",

apps/E2E/wdio.conf.android.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ exports.config = {
3434
bail: 1, // If you only want to run your tests until a specific amount of tests have failed use bail (default is 0 - don't bail, run all tests).
3535
waitforTimeout: defaultWaitForTimeout, // Default timeout for all waitForXXX commands.
3636
connectionRetryTimeout: defaultConnectionRetryTimeout, // Timeout for any WebDriver request to a driver or grid.
37-
connectionRetryCount: 2, // Maximum count of request retries to the Selenium server.
3837
specFileRetries: 2, // The number of times to retry the entire spec file when it fails as a whole.
3938

4039
port: 4723, // default appium port
@@ -141,7 +140,7 @@ exports.config = {
141140
/**
142141
* Function to be executed after a test (in Mocha/Jasmine).
143142
*/
144-
afterTest: (test, context, results) => {
143+
afterTest: (test, _context, results) => {
145144
const resultString = results.passed ? 'Passed' : 'Failed';
146145
console.log('\n Test Case: ' + test.description + '. Result: ' + resultString + '\n');
147146

apps/E2E/wdio.conf.ios.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ exports.config = {
3232
bail: 1, // If you only want to run your tests until a specific amount of tests have failed use bail (default is 0 - don't bail, run all tests).
3333
waitforTimeout: defaultWaitForTimeout, // Default timeout for all waitForXXX commands.
3434
connectionRetryTimeout: defaultConnectionRetryTimeout, // Timeout for any WebDriver request to a driver or grid.
35-
connectionRetryCount: 2, // Maximum count of request retries to the Selenium server.
3635
specFileRetries: 2, // The number of times to retry the entire spec file when it fails as a whole.
3736

3837
port: 4723, // default appium port
@@ -139,7 +138,7 @@ exports.config = {
139138
/**
140139
* Function to be executed after a test (in Mocha/Jasmine).
141140
*/
142-
afterTest: (test, context, results) => {
141+
afterTest: (test, _context, results) => {
143142
const resultString = results.passed ? 'Passed' : 'Failed';
144143
console.log(`\nTest Case: "${test.description}".\nResult: "${resultString}".\nDuration: "${(results.duration / 600).toFixed(2)}s". \n`);
145144

apps/E2E/wdio.conf.macos.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ exports.config = {
3131
bail: 1, // If you only want to run your tests until a specific amount of tests have failed use bail (default is 0 - don't bail, run all tests).
3232
waitforTimeout: defaultWaitForTimeout, // Default timeout for all waitForXXX commands.
3333
connectionRetryTimeout: defaultConnectionRetryTimeout, // Timeout for any WebDriver request to a driver or grid.
34-
connectionRetryCount: 2, // Maximum count of request retries to the Selenium server.
3534
specFileRetries: 1, // The number of times to retry the entire spec file when it fails as a whole.
3635

3736
port: 4723, // default appium port
@@ -154,7 +153,7 @@ exports.config = {
154153
/**
155154
* Function to be executed after a test (in Mocha/Jasmine).
156155
*/
157-
afterTest: (test, context, results) => {
156+
afterTest: (test, _context, results) => {
158157
const resultString = results.passed ? 'Passed' : 'Failed';
159158
console.log(`\nTest Case: "${test.description}".\nResult: "${resultString}".\nDuration: "${(results.duration / 600).toFixed(2)}s". \n`);
160159

0 commit comments

Comments
 (0)