chore: add strict-boolean-expressions rule (#27852)

## Description
Add `@typescript-eslint/strict-boolean-expressions` eslint rule for
packages.
This commit is contained in:
Valera Melnikov 2023-10-06 16:05:32 +03:00 committed by GitHub
parent e32e51b2fe
commit 22e10c6fdb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 109 additions and 78 deletions

View File

@ -3,6 +3,12 @@
"$schema": "http://json.schemastore.org/eslintrc",
"root": true,
"parser": "@typescript-eslint/parser",
"ignorePatterns": [
"jest.config.js",
"jest.setup.ts",
"rollup.config.js",
".eslintrc.js"
],
"plugins": [
"react",
"@typescript-eslint",
@ -25,9 +31,11 @@
"sourceType": "module", // Allows for the use of imports
"ecmaFeatures": {
"jsx": true // Allows for the parsing of JSX
}
},
"project": "./tsconfig.json"
},
"rules": {
"@typescript-eslint/strict-boolean-expressions": "error",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-unused-vars": "error",
"jest/no-focused-tests": "error",

View File

@ -13,6 +13,7 @@ const baseNoRestrictedImports =
const eslintConfig = {
extends: ["./.eslintrc.base.json"],
rules: {
"@typescript-eslint/strict-boolean-expressions": "off",
"@typescript-eslint/no-explicit-any": "off",
"react/display-name": "off",
"react/prop-types": "off",

View File

@ -1,7 +1,7 @@
{
"src/**/*.{js,ts,tsx}": ["npx eslint --fix"],
"src/**/*.{js,ts,tsx,css,md,json}": ["npx prettier --write"],
"cypress/**/*.{js,ts}": ["npx eslint --fix"],
"cypress/**/*.{js,ts}": ["cd ./cypress && npx eslint -c .eslintrc.json --fix"],
"cypress/**/*.{js,ts,json}": ["npx prettier --write"],
"packages/**/*.{js,ts,tsx}": ["npx eslint --fix"],
"packages/**/*.{js,ts,tsx,css,mdx,json}": ["npx prettier --write"]

View File

@ -1,9 +1,11 @@
{
"extends": ["../.eslintrc.base.json"],
"ignorePatterns": ["locators", "fixtures"],
"env": {
"cypress/globals": true
},
"rules": {
"@typescript-eslint/strict-boolean-expressions": "off",
"@typescript-eslint/no-array-constructor": "off",
"@typescript-eslint/no-namespace": "off",
"@typescript-eslint/no-unused-vars": "off",

View File

@ -32,5 +32,5 @@
"noFallthroughCasesInSwitch": true,
"types": ["cypress", "node", "cypress-tags", "cypress-real-events"]
},
"include": ["/**/*.ts", "../packages/rts/src/version.js"]
"include": ["./", "../packages/rts/src/version.js"]
}

View File

@ -34,7 +34,7 @@
"generate:widget": "plop --plopfile generators/index.js",
"postinstall": "node cypress/apply-patches.js && yarn init-husky",
"storybook": "yarn workspace @design-system/storybook storybook",
"lint": "eslint --cache --ext .js,.ts,.tsx ./src && eslint --cache --ext .js,.ts ./cypress",
"lint": "eslint --cache ./src && cd ./cypress && eslint -c .eslintrc.json --cache ./",
"g:lint": "cd $INIT_CWD && eslint --cache .",
"lint:ci": "yarn workspaces foreach -ptv run lint",
"prettier": "prettier --check ./src && prettier --check ./cypress",

View File

@ -2,6 +2,7 @@
"extends": ["../../.eslintrc.base.json"],
"ignorePatterns": ["build"],
"rules": {
"@typescript-eslint/strict-boolean-expressions": "off",
"@typescript-eslint/no-explicit-any": "off"
}
}

View File

@ -20,7 +20,13 @@ export type ButtonRef = React.Ref<HTMLButtonElement>;
type ButtonRefObject = React.RefObject<HTMLButtonElement>;
const _Button = (props: ButtonProps, ref: ButtonRef) => {
const { autoFocus, children, className, draggable, isDisabled } = props;
const {
autoFocus,
children,
className,
draggable = false,
isDisabled = false,
} = props;
const { hoverProps, isHovered } = useHover({ isDisabled });
const { focusProps, isFocusVisible } = useFocusRing({ autoFocus });
const { buttonProps, isPressed } = useButton(props, ref as ButtonRefObject);
@ -28,8 +34,8 @@ const _Button = (props: ButtonProps, ref: ButtonRef) => {
return (
<button
{...mergeProps(buttonProps, hoverProps, focusProps)}
aria-busy={props["aria-busy"] ? true : undefined}
aria-disabled={props["aria-disabled"] ? true : undefined}
aria-busy={props["aria-busy"] ?? undefined}
aria-disabled={props["aria-disabled"] ?? undefined}
className={className}
data-active={isPressed ? "" : undefined}
data-disabled={isDisabled ? "" : undefined}

View File

@ -51,7 +51,7 @@ const _Checkbox = (props: CheckboxProps, ref: CheckboxRef) => {
const context = useContext(CheckboxGroupContext) as CheckboxGroupContextType;
const isDisabled = isDisabledProp || context?.isDisabled;
const { hoverProps, isHovered } = useHover({ isDisabled });
const { inputProps } = context?.state
const { inputProps } = Boolean(context?.state)
? // eslint-disable-next-line react-hooks/rules-of-hooks
useCheckboxGroupItem(
{
@ -74,7 +74,7 @@ const _Checkbox = (props: CheckboxProps, ref: CheckboxRef) => {
const dataState = isIndeterminate
? "indeterminate"
: inputProps.checked
: Boolean(inputProps.checked)
? "checked"
: "unchecked";
@ -82,7 +82,7 @@ const _Checkbox = (props: CheckboxProps, ref: CheckboxRef) => {
<label
{...hoverProps}
className={className}
data-disabled={isDisabled ? "" : undefined}
data-disabled={Boolean(isDisabled) ? "" : undefined}
data-focused={isFocusVisible ? "" : undefined}
data-hovered={isHovered ? "" : undefined}
data-invalid={validationState === "invalid" ? "" : undefined}

View File

@ -20,7 +20,12 @@ export interface CheckboxGroupProps
}
const _CheckboxGroup = (props: CheckboxGroupProps, ref: CheckboxGroupRef) => {
const { children, className, isDisabled, orientation = "vertical" } = props;
const {
children,
className,
isDisabled = false,
orientation = "vertical",
} = props;
const domRef = useDOMRef(ref);
const state = useCheckboxGroupState(props);
const { descriptionProps, errorMessageProps, groupProps, labelProps } =
@ -38,7 +43,7 @@ const _CheckboxGroup = (props: CheckboxGroupProps, ref: CheckboxGroupRef) => {
>
<div
{...groupProps}
data-disabled={props.isDisabled ? "" : undefined}
data-disabled={isDisabled ? "" : undefined}
data-field-group=""
data-orientation={orientation}
>

View File

@ -19,7 +19,7 @@ function _HelpText(props: HelpTextProps, ref: DOMRef<HTMLDivElement>) {
validationState,
} = props;
const domRef = useDOMRef(ref);
const isErrorMessage = errorMessage && validationState === "invalid";
const isErrorMessage = Boolean(errorMessage) && validationState === "invalid";
return (
<div ref={domRef}>

View File

@ -27,7 +27,7 @@ const _Field = (props: FieldProps, ref: FieldRef) => {
elementType,
errorMessage,
errorMessageProps = {},
isDisabled,
isDisabled = false,
label,
labelProps,
validationState,
@ -39,7 +39,8 @@ const _Field = (props: FieldProps, ref: FieldRef) => {
fieldType = "field",
} = props;
const hasHelpText =
!!description || (errorMessage && validationState === "invalid");
Boolean(description) ||
(Boolean(errorMessage) && validationState === "invalid");
const renderHelpText = () => {
return (
@ -56,12 +57,12 @@ const _Field = (props: FieldProps, ref: FieldRef) => {
const labelAndContextualHelp = (
<div data-field-label-wrapper="">
{label && (
{Boolean(label) && (
<Label {...labelProps} elementType={elementType}>
{label}
</Label>
)}
{label && contextualHelp}
{Boolean(label) && contextualHelp}
</div>
);

View File

@ -19,7 +19,7 @@ function _HelpText(props: HelpTextProps, ref: DOMRef<HTMLDivElement>) {
validationState,
} = props;
const domRef = useDOMRef(ref);
const isErrorMessage = errorMessage && validationState === "invalid";
const isErrorMessage = Boolean(errorMessage) && validationState === "invalid";
return (
<div ref={domRef}>

View File

@ -30,7 +30,9 @@ const _Label = (props: LabelProps, ref: DOMRef<HTMLLabelElement>) => {
<ElementType
data-field-label=""
{...filterDOMProps(otherProps)}
htmlFor={ElementType === "label" ? labelFor || htmlFor : undefined}
htmlFor={
ElementType === "label" ? Boolean(labelFor) || htmlFor : undefined
}
onClick={onClick}
ref={domRef}
>

View File

@ -21,13 +21,11 @@ export function Icon(props: IconProps) {
...otherProps
} = props;
const ariaHidden = !ariaHiddenProp ? undefined : ariaHiddenProp;
return React.cloneElement(children, {
...filterDOMProps(otherProps),
focusable: "false",
"aria-label": ariaLabel,
"aria-hidden": ariaLabel ? ariaHidden || undefined : true,
"aria-hidden": Boolean(ariaLabel) ? ariaHiddenProp ?? undefined : true,
role,
"data-icon": "",
className,

View File

@ -50,7 +50,7 @@ const _Radio = (props: RadioProps, ref: RadioRef) => {
<label
{...hoverProps}
className={className}
data-disabled={isDisabled ? "" : undefined}
data-disabled={Boolean(isDisabled) ? "" : undefined}
data-focused={isFocusVisible ? "" : undefined}
data-hovered={isHovered ? "" : undefined}
data-invalid={validationState === "invalid" ? "" : undefined}

View File

@ -44,7 +44,7 @@ const _Switch = (props: SwitchProps, ref: SwitchRef) => {
const context = useContext(CheckboxGroupContext) as CheckboxGroupContextType;
const isDisabled = isDisabledProp || context?.isDisabled;
const { hoverProps, isHovered } = useHover({ isDisabled });
const { inputProps } = context?.state
const { inputProps } = Boolean(context?.state)
? // eslint-disable-next-line react-hooks/rules-of-hooks
useCheckboxGroupItem(
{
@ -65,13 +65,13 @@ const _Switch = (props: SwitchProps, ref: SwitchRef) => {
: // eslint-disable-next-line react-hooks/rules-of-hooks
useSwitch(props, state, inputRef);
const dataState = inputProps.checked ? "checked" : "unchecked";
const dataState = Boolean(inputProps.checked) ? "checked" : "unchecked";
return (
<label
{...hoverProps}
className={className}
data-disabled={isDisabled ? "" : undefined}
data-disabled={Boolean(isDisabled) ? "" : undefined}
data-focused={isFocusVisible ? "" : undefined}
data-hovered={isHovered ? "" : undefined}
data-invalid={validationState === "invalid" ? "" : undefined}

View File

@ -32,7 +32,7 @@ function TextArea(props: TextAreaProps, ref: TextAreaRef) {
const onHeightChange = useCallback(() => {
// Quiet textareas always grow based on their text content.
// Standard textareas also grow by default, unless an explicit height is set.
if (!props.height && inputRef.current) {
if (props.height == null && inputRef.current) {
const input = inputRef.current;
const prevAlignment = input.style.alignSelf;
const prevOverflow = input.style.overflow;
@ -60,7 +60,7 @@ function TextArea(props: TextAreaProps, ref: TextAreaRef) {
}
}, [onHeightChange, inputValue, inputRef.current]);
if (props.placeholder) {
if (props.placeholder != null) {
// eslint-disable-next-line no-console
console.warn(
"Placeholders are deprecated due to accessibility issues. Please use help text instead. See the docs for details: https://react-spectrum.adobe.com/react-spectrum/TextArea.html#help-text",

View File

@ -14,7 +14,7 @@ function TextInput(props: TextInputProps, ref: TextInputRef) {
const { descriptionProps, errorMessageProps, inputProps, labelProps } =
useTextField(rest, inputRef);
if (props.placeholder) {
if (props.placeholder != null) {
// eslint-disable-next-line no-console
console.warn(
"Placeholders are deprecated due to accessibility issues. Please use help text instead. See the docs for details: https://react-spectrum.adobe.com/react-spectrum/TextField.html#help-text",

View File

@ -17,10 +17,10 @@ function TextInputBase(props: TextInputBaseProps, ref: Ref<HTMLDivElement>) {
inputClassName,
inputProps,
inputRef: userInputRef,
isDisabled,
isLoading,
isDisabled = false,
isLoading = false,
labelProps,
multiLine,
multiLine = false,
onBlur,
onFocus,
startIcon,
@ -32,8 +32,10 @@ function TextInputBase(props: TextInputBaseProps, ref: Ref<HTMLDivElement>) {
const defaultInputRef = useRef<HTMLInputElement | HTMLTextAreaElement>(null);
const inputRef = userInputRef || defaultInputRef;
const ElementType: React.ElementType = multiLine ? "textarea" : "input";
const isInvalid = validationState === "invalid" && !isDisabled;
const ElementType: React.ElementType = Boolean(multiLine)
? "textarea"
: "input";
const isInvalid = validationState === "invalid" && !Boolean(isDisabled);
const { focusProps, isFocusVisible } = useFocusRing({
isTextInput: true,

View File

@ -26,7 +26,7 @@ const _TooltipTrigger = (
"data-state": context.open ? "open" : "closed",
// when the trigger is disabled, we want to make sure that the tooltip is
// accessible with keyboard but visually disabled only
visuallyDisabled: children.props.isDisabled ? true : undefined,
visuallyDisabled: Boolean(children.props.isDisabled) ? true : undefined,
}),
);
}

View File

@ -56,7 +56,7 @@ export function useTheme(props: UseThemeProps = {}) {
}, [colorMode]);
useEffect(() => {
if (borderRadius) {
if (borderRadius != null) {
tokensAccessor.updateBorderRadius({
1: borderRadius,
});
@ -71,7 +71,7 @@ export function useTheme(props: UseThemeProps = {}) {
}, [borderRadius]);
useEffect(() => {
if (seedColor) {
if (seedColor != null) {
let color;
try {
@ -107,7 +107,7 @@ export function useTheme(props: UseThemeProps = {}) {
}, [fontFamily]);
useEffect(() => {
if (rootUnitRatioProp) {
if (rootUnitRatioProp != null) {
setRootUnitRatio(rootUnitRatioProp);
}
}, [rootUnitRatioProp]);

View File

@ -5,7 +5,7 @@ export const cssRule = (tokens: Theme) => {
let styles = "";
Object.values(tokens).forEach((token) => {
if (!token) return;
if (token == null) return;
Object.keys(token).forEach((key) => {
//@ts-expect-error: type mismatch

View File

@ -1,6 +1,7 @@
{
"extends": ["../../../.eslintrc.base.json"],
"rules": {
"@typescript-eslint/strict-boolean-expressions": "off",
"@typescript-eslint/no-explicit-any": "off",
"react/display-name": "off"
}

View File

@ -20,12 +20,13 @@ const _Button = (props: ButtonProps, ref: HeadlessButtonRef) => {
color = "accent",
icon: Icon,
iconPosition = "start",
isLoading,
isDisabled = false,
isLoading = false,
loadingText = "Loading...",
// eslint-disable-next-line -- TODO add onKeyUp when the bug is fixed https://github.com/adobe/react-spectrum/issues/4350
onKeyUp,
variant = "filled",
visuallyDisabled,
visuallyDisabled = false,
...rest
} = props;
const { visuallyHiddenProps } = useVisuallyHidden();
@ -39,7 +40,7 @@ const _Button = (props: ButtonProps, ref: HeadlessButtonRef) => {
<Icon />
</HeadlessIcon>
)}
{children && (
{Boolean(children) && (
<Text fontWeight={600} lineClamp={1} textAlign="center">
{children}
</Text>
@ -58,7 +59,7 @@ const _Button = (props: ButtonProps, ref: HeadlessButtonRef) => {
<HeadlessButton
aria-busy={isLoading ? true : undefined}
aria-disabled={
visuallyDisabled || isLoading || props.isDisabled ? true : undefined
visuallyDisabled || isLoading || isDisabled ? true : undefined
}
className={clsx(styles.button, getTypographyClassName("body"))}
data-button=""
@ -67,6 +68,7 @@ const _Button = (props: ButtonProps, ref: HeadlessButtonRef) => {
data-loading={isLoading ? "" : undefined}
data-variant={variant}
draggable
isDisabled={isDisabled}
ref={ref}
{...rest}
>
@ -83,9 +85,10 @@ export const Button = forwardRef(_Button);
* when the button is visually disabled
*/
const useVisuallyDisabled = (props: ButtonProps) => {
const { isLoading = false, visuallyDisabled = false } = props;
let computedProps = props;
if (props.visuallyDisabled || props.isLoading) {
if (visuallyDisabled || isLoading) {
computedProps = {
...props,
isDisabled: false,

View File

@ -25,7 +25,7 @@ const _Checkbox = (props: CheckboxProps, ref: HeadlessCheckboxRef) => {
inlineLabelStyles["inline-label"],
)}
>
{children && <Text>{children}</Text>}
{Boolean(children) && <Text>{children}</Text>}
</HeadlessCheckbox>
);
};

View File

@ -18,10 +18,10 @@ const _CheckboxGroup = (
ref: HeadlessCheckboxGroupRef,
) => {
const { errorMessage, label, ...rest } = props;
const wrappedErrorMessage = errorMessage && (
const wrappedErrorMessage = Boolean(errorMessage) && (
<Text variant="footnote">{errorMessage}</Text>
);
const wrappedLabel = label && <Text>{label}</Text>;
const wrappedLabel = Boolean(label) && <Text>{label}</Text>;
return (
<HeadlessCheckboxGroup

View File

@ -141,5 +141,5 @@ const cssVarValue = (value: CssVarValues) => {
};
const hiddenValue = (value: FlexCssProps["isHidden"]) => {
return value ? "none" : "flex";
return Boolean(value) ? "none" : "flex";
};

View File

@ -1,2 +0,0 @@
export { IconButton } from "./IconButton";
export type { IconButtonProps } from "./IconButton";

View File

@ -23,7 +23,7 @@ const _Radio = (props: RadioProps, ref: HeadlessRadioRef) => {
ref={ref}
{...rest}
>
{children && <Text>{children}</Text>}
{Boolean(children) && <Text>{children}</Text>}
</HeadlessRadio>
);
};

View File

@ -15,8 +15,10 @@ export interface RadioGroupProps extends HeadlessRadioGroupProps {
const _RadioGroup = (props: RadioGroupProps, ref: HeadlessRadioGroupRef) => {
const { errorMessage, label, ...rest } = props;
const wrappedErrorMessage = errorMessage && <Text>{errorMessage}</Text>;
const wrappedLabel = label && <Text>{label}</Text>;
const wrappedErrorMessage = Boolean(errorMessage) && (
<Text>{errorMessage}</Text>
);
const wrappedLabel = Boolean(label) && <Text>{label}</Text>;
return (
<HeadlessRadioGroup

View File

@ -23,7 +23,7 @@ const _Switch = (props: SwitchProps, ref: HeadlessSwitchRef) => {
ref={ref}
{...rest}
>
{children && <Text>{children}</Text>}
{Boolean(children) && <Text>{children}</Text>}
</HeadlessSwitch>
);
};

View File

@ -25,7 +25,7 @@ const _Text = (props: TextProps, ref: Ref<HTMLParagraphElement>) => {
const getFontWeight = (
fontWeight?: keyof typeof TYPOGRAPHY_FONT_WEIGHTS,
isBold?: boolean,
isBold = false,
) => {
if (fontWeight) return fontWeight;

View File

@ -34,7 +34,7 @@ const _TextArea = (props: TextAreaProps, ref: HeadlessTextAreaRef) => {
...rest
} = props;
const wrappedLabel = label && (
const wrappedLabel = Boolean(label) && (
<Label
includeNecessityIndicatorInAccessibilityName={
includeNecessityIndicatorInAccessibilityName
@ -45,10 +45,10 @@ const _TextArea = (props: TextAreaProps, ref: HeadlessTextAreaRef) => {
/>
);
const wrappedDescription = description && (
const wrappedDescription = Boolean(description) && (
<Text variant="footnote">{description}</Text>
);
const wrappedErrorMessage = errorMessage && (
const wrappedErrorMessage = Boolean(errorMessage) && (
<Text variant="footnote">{errorMessage}</Text>
);

View File

@ -1,2 +0,0 @@
export { TextArea } from "./TextArea";
export type { TextAreaProps } from "./TextArea";

View File

@ -8,7 +8,7 @@ export type LabelProps = TextInputProps;
const _Label = (props: LabelProps) => {
const {
includeNecessityIndicatorInAccessibilityName,
isRequired,
isRequired = false,
label,
necessityIndicator = "icon",
} = props;
@ -16,7 +16,9 @@ const _Label = (props: LabelProps) => {
const icon = (
<span
aria-label={
includeNecessityIndicatorInAccessibilityName ? "(required)" : undefined
Boolean(includeNecessityIndicatorInAccessibilityName)
? "(required)"
: undefined
}
data-field-necessity-indicator-icon=""
>
@ -36,7 +38,7 @@ const _Label = (props: LabelProps) => {
{necessityIndicator === "label" && (
<span
aria-hidden={
!includeNecessityIndicatorInAccessibilityName
includeNecessityIndicatorInAccessibilityName == null
? isRequired
: undefined
}

View File

@ -38,6 +38,7 @@ const _TextInput = (props: TextInputProps, ref: HeadlessTextInputRef) => {
endIcon,
errorMessage,
includeNecessityIndicatorInAccessibilityName,
isLoading = false,
isRequired,
label,
loaderPosition = "auto",
@ -48,7 +49,7 @@ const _TextInput = (props: TextInputProps, ref: HeadlessTextInputRef) => {
} = props;
const [showPassword, togglePassword] = useState(false);
const wrappedLabel = label && (
const wrappedLabel = Boolean(label) && (
<Label
includeNecessityIndicatorInAccessibilityName={
includeNecessityIndicatorInAccessibilityName
@ -59,15 +60,15 @@ const _TextInput = (props: TextInputProps, ref: HeadlessTextInputRef) => {
/>
);
const contextualHelp = label && contextualHelpProp && (
const contextualHelp = Boolean(label) && Boolean(contextualHelpProp) && (
<ContextualHelp contextualHelp={contextualHelpProp} />
);
const wrappedDescription = description && (
const wrappedDescription = Boolean(description) && (
<Text variant="footnote">{description}</Text>
);
const wrappedErrorMessage = errorMessage && (
const wrappedErrorMessage = Boolean(errorMessage) && (
<Text variant="footnote">{errorMessage}</Text>
);
@ -77,9 +78,9 @@ const _TextInput = (props: TextInputProps, ref: HeadlessTextInputRef) => {
const renderStartIcon = () => {
const showLoadingIndicator =
props.isLoading &&
isLoading &&
(loaderPosition === "start" ||
Boolean(startIcon && loaderPosition !== "end"));
(Boolean(startIcon) && loaderPosition !== "end"));
if (!showLoadingIndicator) return startIcon;
@ -101,9 +102,9 @@ const _TextInput = (props: TextInputProps, ref: HeadlessTextInputRef) => {
}
const showLoadingIndicator =
props.isLoading &&
isLoading &&
(loaderPosition === "end" ||
Boolean(loaderPosition === "auto" && !startIcon));
Boolean(loaderPosition === "auto" && Boolean(startIcon)));
if (!showLoadingIndicator) return endIcon;

View File

@ -11,7 +11,7 @@ export type TooltipProps = {
export function Tooltip(props: TooltipProps) {
const { children, tooltip, ...rest } = props;
if (!tooltip) return children;
if (tooltip == null) return children;
return (
<TooltipRoot {...rest}>

View File

@ -174,10 +174,10 @@ export const ColorGrid = (props: any) => {
>
<button
className={styles["color-grid"]}
data-active={isActive ? "" : undefined}
data-disabled={isDisabled ? "" : undefined}
data-focused={isFocused ? "" : undefined}
data-hovered={isHovered ? "" : undefined}
data-active={Boolean(isActive) ? "" : undefined}
data-disabled={Boolean(isDisabled) ? "" : undefined}
data-focused={Boolean(isFocused) ? "" : undefined}
data-hovered={Boolean(isHovered) ? "" : undefined}
data-variant={variant}
>
<Text lineClamp={2} textAlign="center" variant="footnote">

View File

@ -1,7 +1,8 @@
{
"extends": ["../../.eslintrc.base.json"],
"ignorePatterns": ["dist"],
"ignorePatterns": ["dist", "server.test.ts"],
"rules": {
"@typescript-eslint/strict-boolean-expressions": "off",
"@typescript-eslint/no-explicit-any": "off"
}
}