Migrate from ESLint to OxLint (#18443)

## Summary

Fully replaces ESLint with OxLint across the entire monorepo:

- **Replaced all ESLint configs** (`eslint.config.mjs`) with OxLint
configs (`.oxlintrc.json`) for every package: `twenty-front`,
`twenty-server`, `twenty-emails`, `twenty-ui`, `twenty-shared`,
`twenty-sdk`, `twenty-zapier`, `twenty-docs`, `twenty-website`,
`twenty-apps/*`, `create-twenty-app`
- **Migrated custom lint rules** from ESLint plugin format to OxLint JS
plugin system (`@oxlint/plugins`), including
`styled-components-prefixed-with-styled`, `no-hardcoded-colors`,
`sort-css-properties-alphabetically`,
`graphql-resolvers-should-be-guarded`,
`rest-api-methods-should-be-guarded`, `max-consts-per-file`, and
Jotai-related rules
- **Migrated custom rule tests** from ESLint `RuleTester` + Jest to
`oxlint/plugins-dev` `RuleTester` + Vitest
- **Removed all ESLint dependencies** from `package.json` files and
regenerated lockfiles
- **Updated Nx targets** (`lint`, `lint:diff-with-main`, `fmt`) in
`nx.json` and per-project `project.json` to use `oxlint` commands with
proper `dependsOn` for plugin builds
- **Updated CI workflows** (`.github/workflows/ci-*.yaml`) — no more
ESLint executor
- **Updated IDE setup**: replaced `dbaeumer.vscode-eslint` with
`oxc.oxc-vscode` extension, configured `source.fixAll.oxc` and
format-on-save with Prettier
- **Replaced all `eslint-disable` comments** with `oxlint-disable`
equivalents across the codebase
- **Updated docs** (`twenty-docs`) to reference OxLint instead of ESLint
- **Renamed** `twenty-eslint-rules` package to `twenty-oxlint-rules`

### Temporarily disabled rules (tracked in `OXLINT_MIGRATION_TODO.md`)

| Rule | Package | Violations | Auto-fixable |
|------|---------|-----------|-------------|
| `twenty/sort-css-properties-alphabetically` | twenty-front | 578 | Yes
|
| `typescript/consistent-type-imports` | twenty-server | 3814 | Yes |
| `twenty/max-consts-per-file` | twenty-server | 94 | No |

### Dropped plugins (no OxLint equivalent)

`eslint-plugin-project-structure`, `lingui/*`, `@stylistic/*`,
`import/order`, `prefer-arrow/prefer-arrow-functions`,
`eslint-plugin-mdx`, `@next/eslint-plugin-next`,
`eslint-plugin-storybook`, `eslint-plugin-react-refresh`. Partial
coverage for `jsx-a11y` and `unused-imports`.

### Additional fixes (pre-existing issues exposed by merge)

- Fixed `EmailThreadPreview.tsx` broken import from main rename
(`useOpenEmailThreadInSidePanel`)
- Restored truthiness guard in `getActivityTargetObjectRecords.ts`
- Fixed `AgentTurnResolver` return types to match entity (virtual
`fileMediaType`/`fileUrl` are resolved via `@ResolveField()`)

## Test plan

- [x] `npx nx lint twenty-front` passes
- [x] `npx nx lint twenty-server` passes
- [x] `npx nx lint twenty-docs` passes
- [x] Custom oxlint rules validated with Vitest: `npx nx test
twenty-oxlint-rules`
- [x] `npx nx typecheck twenty-front` passes
- [x] `npx nx typecheck twenty-server` passes
- [x] CI workflows trigger correctly with `dependsOn:
["twenty-oxlint-rules:build"]`
- [x] IDE linting works with `oxc.oxc-vscode` extension
This commit is contained in:
Charles Bochet
2026-03-06 01:03:50 +01:00
committed by GitHub
parent b421efbff7
commit 9d57bc39e5
880 changed files with 4711 additions and 9687 deletions
@@ -1,4 +1,5 @@
import { type FieldMultiSelectValue } from '@/object-record/record-field/ui/types/FieldMetadata';
import { isDefined } from 'twenty-shared/utils';
import { styled } from '@linaria/react';
import { Tag } from 'twenty-ui/components';
import { type SelectOption } from 'twenty-ui/input';
@@ -27,7 +28,7 @@ export const MultiSelectDisplay = ({
? options?.filter((option) => values.includes(option.value))
: [];
if (!selectedOptions) return null;
if (!isDefined(selectedOptions)) return null;
return (
<StyledContainer>
@@ -1,3 +1,4 @@
import { isDefined } from 'twenty-shared/utils';
import { styled } from '@linaria/react';
import { useContext, useEffect, useRef, useState } from 'react';
@@ -122,7 +123,7 @@ export const CurrencyInput = ({
onChange={handleCurrencyChange}
/>
<StyledIcon>
{Icon && (
{isDefined(Icon) && (
<Icon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />
)}
</StyledIcon>
@@ -1,6 +1,6 @@
import { styled } from '@linaria/react';
// eslint-disable-next-line twenty/styled-components-prefixed-with-styled
// oxlint-disable-next-line twenty/styled-components-prefixed-with-styled
export const FieldInputContainer = styled.div`
align-items: center;
display: flex;
@@ -1,3 +1,4 @@
import { isDefined } from 'twenty-shared/utils';
import { css } from '@linaria/core';
import { styled } from '@linaria/react';
import React, { type ReactNode, useCallback, useMemo, useState } from 'react';
@@ -212,7 +213,7 @@ export const IconPicker = ({
const icons = getIcons();
const totalMatchingIconsCount = useMemo(() => {
if (!icons) return 0;
if (!isDefined(icons)) return 0;
return Object.keys(icons).filter((iconKey) => {
const iconLabel = convertIconKeyToLabel(iconKey)
@@ -265,7 +266,8 @@ export const IconPicker = ({
.map(({ iconKey }) => iconKey);
const isSelectedIconMatchingFilter =
selectedIconKey && filteredAndSortedIconKeys.includes(selectedIconKey);
isDefined(selectedIconKey) &&
filteredAndSortedIconKeys.includes(selectedIconKey);
return isSelectedIconMatchingFilter
? [
@@ -306,7 +308,7 @@ export const IconPicker = ({
dropdownId={dropdownId}
dropdownOffset={dropdownOffset}
clickableComponent={
clickableComponent || (
clickableComponent ?? (
<IconButton
ariaLabel={t`Click to select icon ${iconAriaLabel}`}
disabled={disabled}
@@ -1,5 +1,6 @@
import { styled } from '@linaria/react';
import React from 'react';
import { isDefined } from 'twenty-shared/utils';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledInputErrorHelper = styled.div`
@@ -15,7 +16,7 @@ export const InputErrorHelper = ({
children?: React.ReactNode;
}) => (
<div>
{children && (
{isDefined(children) && (
<StyledInputErrorHelper aria-live="polite">
{children}
</StyledInputErrorHelper>
@@ -64,7 +64,7 @@ export const SelectInput = ({
option.value !== selectedOption?.value &&
normalizeSearchText(option.label).includes(searchTerm)
);
}) || []
})
);
}, [options, searchFilter, selectedOption?.value]);
@@ -122,7 +122,7 @@ export const SettingsTextInput = ({
return (
<TextInput
ref={inputRef}
// eslint-disable-next-line react/jsx-props-no-spreading
// oxlint-disable-next-line react/jsx-props-no-spreading
{...props}
dataTestId={dataTestId}
onFocus={handleFocus}
@@ -1,4 +1,5 @@
import { InputErrorHelper } from '@/ui/input/components/InputErrorHelper';
import { isDefined } from 'twenty-shared/utils';
import { InputLabel } from '@/ui/input/components/InputLabel';
import { css } from '@linaria/core';
import { styled } from '@linaria/react';
@@ -143,7 +144,9 @@ const StyledInput = styled.input<
? `calc(${themeCssVariables.spacing[3]} + 16px)`
: themeCssVariables.spacing[2]};
width: ${({ width }) =>
width ? `calc(${width}px + ${themeCssVariables.spacing[0.5]})` : '100%'};
isDefined(width)
? `calc(${width}px + ${themeCssVariables.spacing[0.5]})`
: '100%'};
max-width: ${({ autoGrow }) => (autoGrow ? '100%' : 'none')};
text-overflow: ellipsis;
&::placeholder,
@@ -336,7 +339,7 @@ const TextInputComponent = forwardRef<
id={instanceId}
width={width}
data-testid={dataTestId}
autoComplete={autoComplete || 'off'}
autoComplete={autoComplete ?? 'off'}
ref={combinedRef}
tabIndex={tabIndex ?? 0}
onFocus={handleFocus}
@@ -457,10 +460,10 @@ const TextInputWithAutoGrowWrapper = forwardRef<
{props.autoGrow ? (
<StyledAutogrowWrapper
sizeVariant={props.sizeVariant}
node={props.value || props.placeholder}
node={props.value ?? props.placeholder}
>
<TextInputComponent
// eslint-disable-next-line react/jsx-props-no-spreading
// oxlint-disable-next-line react/jsx-props-no-spreading
{...props}
ref={ref}
fullWidth={true}
@@ -468,7 +471,7 @@ const TextInputWithAutoGrowWrapper = forwardRef<
</StyledAutogrowWrapper>
) : (
<TextInputComponent
// eslint-disable-next-line react/jsx-props-no-spreading
// oxlint-disable-next-line react/jsx-props-no-spreading
{...props}
ref={ref}
/>
@@ -18,7 +18,7 @@ const IconPickerStory = (args: IconPickerStoryProps) => {
return (
<IconPicker
// eslint-disable-next-line react/jsx-props-no-spreading
// oxlint-disable-next-line react/jsx-props-no-spreading
{...args}
onChange={({ iconKey }) => {
setSelectedIconKey(iconKey);
@@ -33,7 +33,7 @@ const meta: Meta<typeof IconPicker> = {
component: IconPicker,
decorators: [IconsProviderDecorator, ComponentDecorator],
render: (args: IconPickerProps) => (
// eslint-disable-next-line react/jsx-props-no-spreading
// oxlint-disable-next-line react/jsx-props-no-spreading
<IconPickerStory key={args.selectedIconKey ?? 'no-selection'} {...args} />
),
};
@@ -15,7 +15,7 @@ const Render = (args: RenderProps) => {
setValue(value);
};
// eslint-disable-next-line react/jsx-props-no-spreading
// oxlint-disable-next-line react/jsx-props-no-spreading
return <Select {...args} value={value} onChange={handleChange} />;
};
@@ -14,7 +14,7 @@ const Render = (args: RenderProps) => {
setValue(text);
};
// eslint-disable-next-line react/jsx-props-no-spreading
// oxlint-disable-next-line react/jsx-props-no-spreading
return <TextArea {...args} value={value} onChange={handleChange} />;
};
@@ -16,7 +16,7 @@ const Render = (args: RenderProps) => {
setValue(text);
};
// eslint-disable-next-line react/jsx-props-no-spreading
// oxlint-disable-next-line react/jsx-props-no-spreading
return <TextInput {...args} value={value} onChange={handleChange} />;
};
@@ -9,6 +9,7 @@ import { styled } from '@linaria/react';
import { useState } from 'react';
import { type Nullable } from 'twenty-shared/types';
import {
isDefined,
relativeDateFilterSchema,
type RelativeDateFilter,
type RelativeDateFilterDirection,
@@ -55,7 +56,7 @@ export const RelativeDatePickerHeader = ({
const [draftAmountValue, setDraftAmountValue] = useState(amountTextValue);
const isUnitPlural = amount && amount > 1 && direction !== 'THIS';
const isUnitPlural = isDefined(amount) && amount > 1 && direction !== 'THIS';
const unitOptionsSource = allowIntraDayUnits
? RELATIVE_DATETIME_UNITS_SELECT_OPTIONS
: RELATIVE_DATE_UNITS_SELECT_OPTIONS;
@@ -1,4 +1,5 @@
import { useDateTimeFormat } from '@/localization/hooks/useDateTimeFormat';
import { isDefined } from 'twenty-shared/utils';
import { format, isValid } from 'date-fns';
import { getDateTimeFormatStringFoDatePickerInputMask } from '~/utils/date-utils';
@@ -6,7 +7,7 @@ export const useParseJSDateToIMaskDateTimeInputString = () => {
const { dateFormat, timeFormat } = useDateTimeFormat();
const parseJSDateToDateTimeInputString = (date: Date) => {
if (!date || !isValid(date)) {
if (!isDefined(date) || !isValid(date)) {
return '';
}
@@ -45,9 +45,9 @@ export const DraggableItem = ({
return (
<div
ref={draggableProvided.innerRef}
// eslint-disable-next-line react/jsx-props-no-spreading
// oxlint-disable-next-line react/jsx-props-no-spreading
{...draggableProvided.draggableProps}
// eslint-disable-next-line react/jsx-props-no-spreading
// oxlint-disable-next-line react/jsx-props-no-spreading
{...draggableProvided.dragHandleProps}
style={{
...draggableComponentStyles,
@@ -31,7 +31,7 @@ export const DraggableList = ({
{(provided) => (
<div
ref={provided.innerRef}
// eslint-disable-next-line react/jsx-props-no-spreading
// oxlint-disable-next-line react/jsx-props-no-spreading
{...provided.droppableProps}
>
{draggableItems}
@@ -1,5 +1,6 @@
import { styled } from '@linaria/react';
import { type ComponentProps, type MouseEvent } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledHeader = styled.li`
@@ -64,9 +65,11 @@ export const DropdownMenuHeader = ({
}: DropdownMenuHeaderProps) => {
return (
<StyledHeader data-testid={testId} className={className} onClick={onClick}>
{StartComponent && StartComponent}
{isDefined(StartComponent) && StartComponent}
<StyledChildrenWrapper>{children}</StyledChildrenWrapper>
{EndComponent && <StyledEndComponent>{EndComponent}</StyledEndComponent>}
{isDefined(EndComponent) && (
<StyledEndComponent>{EndComponent}</StyledEndComponent>
)}
</StyledHeader>
);
};
@@ -8,6 +8,7 @@ import {
import 'react-phone-number-input/style.css';
import { useRegisterInputEvents } from '@/object-record/record-field/ui/meta-types/input/hooks/useRegisterInputEvents';
import { isDefined } from 'twenty-shared/utils';
import { useCombinedRefs } from '~/hooks/useCombinedRefs';
import { themeCssVariables } from 'twenty-ui/theme-constants';
@@ -140,10 +141,10 @@ export const DropdownMenuInput = forwardRef<
placeholder={placeholder}
onChange={onChange}
ref={combinedRef}
withRightComponent={!!rightComponent}
withRightComponent={isDefined(rightComponent)}
/>
)}
{!!rightComponent && (
{isDefined(rightComponent) && (
<StyledRightContainer>{rightComponent}</StyledRightContainer>
)}
</StyledInputContainer>
@@ -44,7 +44,7 @@ describe('useCloseDropdown', () => {
it('should close dropdown from inside component instance context', async () => {
const { result } = renderHook(
() => {
// eslint-disable-next-line twenty/matching-state-variable
// oxlint-disable-next-line twenty/matching-state-variable
const isOutsideDropdownOpen = useAtomComponentStateValue(
isDropdownOpenComponentState,
outsideDropdownId,
@@ -87,7 +87,7 @@ describe('useCloseDropdown', () => {
it('should close dropdown from outside component instance context', async () => {
const { result } = renderHook(
() => {
// eslint-disable-next-line twenty/matching-state-variable
// oxlint-disable-next-line twenty/matching-state-variable
const isOutsideDropdownOpen = useAtomComponentStateValue(
isDropdownOpenComponentState,
outsideDropdownId,
@@ -42,7 +42,7 @@ describe('useOpenDropdown', () => {
it('should open dropdown from inside component instance context', async () => {
const { result } = renderHook(
() => {
// eslint-disable-next-line twenty/matching-state-variable
// oxlint-disable-next-line twenty/matching-state-variable
const isOutsideDropdownOpen = useAtomComponentStateValue(
isDropdownOpenComponentState,
outsideDropdownId,
@@ -75,7 +75,7 @@ describe('useOpenDropdown', () => {
it('should open dropdown from outside component instance context', async () => {
const { result } = renderHook(
() => {
// eslint-disable-next-line twenty/matching-state-variable
// oxlint-disable-next-line twenty/matching-state-variable
const isOutsideDropdownOpen = useAtomComponentStateValue(
isDropdownOpenComponentState,
outsideDropdownId,
@@ -43,7 +43,7 @@ describe('useToggleDropdown', () => {
it('should toggle dropdown from inside component instance context', async () => {
const { result } = renderHook(
() => {
// eslint-disable-next-line twenty/matching-state-variable
// oxlint-disable-next-line twenty/matching-state-variable
const isOutsideDropdownOpen = useAtomComponentStateValue(
isDropdownOpenComponentState,
outsideDropdownId,
@@ -89,7 +89,7 @@ describe('useToggleDropdown', () => {
it('should toggle dropdown from outside component instance context', async () => {
const { result } = renderHook(
() => {
// eslint-disable-next-line twenty/matching-state-variable
// oxlint-disable-next-line twenty/matching-state-variable
const isOutsideDropdownOpen = useAtomComponentStateValue(
isDropdownOpenComponentState,
outsideDropdownId,
@@ -42,7 +42,7 @@ export const StyledCenteredButton = (
props: React.ComponentProps<typeof Button>,
) => (
<StyledCenteredButtonContainer>
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
{/* oxlint-disable-next-line react/jsx-props-no-spreading */}
<Button {...props} />
</StyledCenteredButtonContainer>
);
@@ -74,7 +74,7 @@ export const StyledConfirmationButton = (
props: React.ComponentProps<typeof Button>,
) => (
<StyledConfirmationButtonContainer>
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
{/* oxlint-disable-next-line react/jsx-props-no-spreading */}
<Button {...props} />
</StyledConfirmationButtonContainer>
);
@@ -1,7 +1,7 @@
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { styled } from '@linaria/react';
// eslint-disable-next-line twenty/styled-components-prefixed-with-styled
// oxlint-disable-next-line twenty/styled-components-prefixed-with-styled
export const OverlayContainer = styled.div<{
borderRadius?: 'sm' | 'md';
hasDangerBorder?: boolean;
@@ -15,6 +15,7 @@ import {
OverflowingTextWithTooltip,
} from 'twenty-ui/display';
import { LightIconButton } from 'twenty-ui/input';
import { isDefined } from 'twenty-shared/utils';
import {
MOBILE_VIEWPORT,
ThemeContext,
@@ -126,7 +127,7 @@ export const PageHeader = ({
<Icon size={theme.icon.size.md} />
</StyledIconContainer>
)}
{title && (
{isDefined(title) && (
<StyledTitleContainer data-testid="top-bar-title">
{typeof title === 'string' ? (
<OverflowingTextWithTooltip text={title} />
@@ -3,6 +3,7 @@ import {
Breadcrumb,
type BreadcrumbProps,
} from '@/ui/navigation/bread-crumb/components/Breadcrumb';
import { isDefined } from 'twenty-shared/utils';
import { styled } from '@linaria/react';
import { type JSX, type ReactNode } from 'react';
import { PageBody } from './PageBody';
@@ -50,7 +51,7 @@ export const SubMenuTopBarContainer = ({
</PageHeader>
<PageBody>
<InformationBannerWrapper />
{(title || reserveTitleSpace) && (
{(isDefined(title) || reserveTitleSpace === true) && (
<StyledTitle reserveTitleSpace={reserveTitleSpace}>
{title}
</StyledTitle>
@@ -13,7 +13,7 @@ export const TableSubRow = ({
...props
}: React.ComponentProps<typeof TableRow> & { children?: ReactNode }) => (
<StyledTableSubRowContainer>
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
{/* oxlint-disable-next-line react/jsx-props-no-spreading */}
<TableRow {...props}>{children}</TableRow>
</StyledTableSubRowContainer>
);
@@ -44,7 +44,7 @@ export const StyledIconChevronDown = ({
...props
}: { disabled?: boolean } & React.ComponentProps<typeof IconChevronDown>) => (
<StyledIconChevronDownContainer disabled={disabled}>
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
{/* oxlint-disable-next-line react/jsx-props-no-spreading */}
<IconChevronDown {...props} />
</StyledIconChevronDownContainer>
);
@@ -351,9 +351,15 @@ export const NavigationDrawerItem = ({
danger={danger}
soon={soon}
as={
to ? (isExternalLink ? 'a' : Link) : rightOptions ? 'div' : undefined
to
? isExternalLink
? 'a'
: Link
: isDefined(rightOptions)
? 'div'
: undefined
}
role={to ? undefined : rightOptions ? 'button' : undefined}
role={to ? undefined : isDefined(rightOptions) ? 'button' : undefined}
to={isExternalLink ? undefined : to}
href={isExternalLink ? to : undefined}
target={isExternalLink ? '_blank' : undefined}
@@ -362,7 +368,7 @@ export const NavigationDrawerItem = ({
indentationLevel={indentationLevel}
isNavigationDrawerExpanded={isNavigationDrawerExpanded}
isDragging={isDragging}
hasRightOptions={!!rightOptions}
hasRightOptions={isDefined(rightOptions)}
isSelectedInEditMode={isSelectedInEditMode}
>
<StyledItemElementsContainer>
@@ -444,7 +450,7 @@ export const NavigationDrawerItem = ({
</NavigationDrawerAnimatedCollapseWrapper>
)}
{rightOptions && (
{isDefined(rightOptions) && (
<NavigationDrawerAnimatedCollapseWrapper>
<StyledRightOptionsContainer
onClick={(e) => {
@@ -83,7 +83,7 @@ export const NavigationDrawerSectionTitle = ({
<StyledLabelContainer onClick={handleTitleClick}>
<Label>{label}</Label>
</StyledLabelContainer>
{rightIcon && (
{isDefined(rightIcon) && (
<StyledRightIcon
isMobile={isMobile}
$alwaysVisible={alwaysShowRightIcon}
@@ -1,4 +1,5 @@
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { isDefined } from 'twenty-shared/utils';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { type AvailableWorkspace } from '~/generated-metadata/graphql';
@@ -11,11 +12,11 @@ export const useFilteredAvailableWorkspaces = () => {
) => {
return availableWorkspaces.filter(
(availableWorkspace) =>
currentWorkspace?.id &&
isDefined(currentWorkspace?.id) &&
availableWorkspace.id !== currentWorkspace.id &&
availableWorkspace.displayName
?.toLowerCase()
.includes(searchValue.toLowerCase()),
.includes(searchValue.toLowerCase()) === true,
);
};
@@ -32,7 +32,7 @@ export const StepBar = ({ activeStep, children }: StepBarProps) => {
}
// If the child is not a Step, return it as-is
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// oxlint-disable-next-line @typescripttypescript/ban-ts-comment
// @ts-expect-error
if (child.type?.displayName !== Step.displayName) {
return child;
@@ -47,7 +47,7 @@ export const useStepBar = ({ initialStep }: StepsOptions) => {
setStep(initialStep);
}
// We only want this to happen on mount
// eslint-disable-next-line react-hooks/exhaustive-deps
// oxlint-disable-next-line react-hooks/exhaustive-deps
}, []);
return {
@@ -10,7 +10,7 @@ export const useSystemColorScheme = (): ColorScheme => {
);
const [preferredColorScheme, setPreferredColorScheme] = useState<ColorScheme>(
!window.matchMedia || !mediaQuery.matches ? 'Light' : 'Dark',
isUndefinedOrNull(window.matchMedia) || !mediaQuery.matches ? 'Light' : 'Dark',
);
useEffect(() => {
@@ -50,7 +50,7 @@ export const useGlobalHotkeys = ({
async (keyboardEvent: KeyboardEvent, hotkeysEvent: any) => {
const pendingHotkey = store.get(pendingHotkeyState.atom);
if (!pendingHotkey) {
if (!isDefined(pendingHotkey)) {
callback(keyboardEvent, hotkeysEvent);
}
@@ -80,7 +80,7 @@ export const useGlobalHotkeysCallback = (
return callback(keyboardEvent, hotkeysEvent);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
// oxlint-disable-next-line react-hooks/exhaustive-deps
[...dependencyArray, store],
);
};
@@ -32,7 +32,7 @@ export const useGlobalHotkeysSequence = (
callback: () => {
setPendingHotkey(firstKey);
},
preventDefault: !!options.preventDefault,
preventDefault: Boolean(options.preventDefault),
});
},
{
@@ -52,7 +52,7 @@ export const useHotkeysOnFocusedElement = ({
keyboardEvent,
hotkeysEvent,
callback: () => {
if (!pendingHotkey) {
if (!isDefined(pendingHotkey)) {
callback(keyboardEvent, hotkeysEvent);
return;
}
@@ -69,7 +69,7 @@ export const useHotkeysOnFocusedElementCallback = (
return callback(keyboardEvent, hotkeysEvent);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
// oxlint-disable-next-line react-hooks/exhaustive-deps
[...dependencyArray, store],
);
};
@@ -118,7 +118,7 @@ export const useListenClickOutside = <T extends Element>({
!isClickedOnExcluded;
if (CLICK_OUTSIDE_DEBUG_MODE) {
// eslint-disable-next-line no-console
// oxlint-disable-next-line no-console
console.log('click outside compare ref', {
listenerId,
shouldTrigger,