Skip to content

Commit

Permalink
Merge branch 'master' of github.com:juliencrn/usehooks-ts
Browse files Browse the repository at this point in the history
  • Loading branch information
juliencrn committed Jan 30, 2024
2 parents c2e0da6 + 0321342 commit eed729f
Show file tree
Hide file tree
Showing 62 changed files with 407 additions and 192 deletions.
7 changes: 7 additions & 0 deletions .changeset/honest-foxes-press.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"usehooks-ts": patch
"www": patch
"eslint-config-custom": patch
---

Make Typescript and typescript-eslint stricter
5 changes: 5 additions & 0 deletions .changeset/two-jars-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"usehooks-ts": minor
---

Drop Map, Set and Date supports in use\*Storage hook's JSON deserializer
3 changes: 3 additions & 0 deletions apps/www/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
module.exports = {
extends: ['next/core-web-vitals', 'custom'],
rules: {
'@typescript-eslint/require-await': 'off',
},
}
2 changes: 2 additions & 0 deletions apps/www/src/app/(marketing)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,10 @@ async function getGitHubStars(): Promise<string | null> {
return null
}

// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const json = await response.json()

// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/dot-notation
return parseInt(json['stargazers_count']).toLocaleString()
} catch (error) {
return null
Expand Down
4 changes: 3 additions & 1 deletion apps/www/src/components/main-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ export function MainNav({ items, children }: MainNavProps) {
) : null}
<button
className="flex items-center space-x-2 md:hidden"
onClick={() => setShowMobileMenu(!showMobileMenu)}
onClick={() => {
setShowMobileMenu(!showMobileMenu)
}}
>
{showMobileMenu ? <Icons.close /> : <Icons.logo />}
<span className="font-bold">Menu</span>
Expand Down
18 changes: 15 additions & 3 deletions apps/www/src/components/mode-toggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,27 @@ export function ModeToggle() {
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme('light')}>
<DropdownMenuItem
onClick={() => {
setTheme('light')
}}
>
<Icons.sun className="mr-2 h-4 w-4" />
<span>Light</span>
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme('dark')}>
<DropdownMenuItem
onClick={() => {
setTheme('dark')
}}
>
<Icons.moon className="mr-2 h-4 w-4" />
<span>Dark</span>
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme('system')}>
<DropdownMenuItem
onClick={() => {
setTheme('system')
}}
>
<Icons.laptop className="mr-2 h-4 w-4" />
<span>System</span>
</DropdownMenuItem>
Expand Down
2 changes: 1 addition & 1 deletion apps/www/src/lib/mdx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,5 @@ export const getPosts = (): Post[] => {
export const getPost = (slug: string): Option<Post> => {
const allPosts = getPosts()
const post = allPosts.find(post => post.slug === slug)
return post || null
return post ?? null
}
60 changes: 33 additions & 27 deletions packages/eslint-config-custom/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,24 @@ module.exports = {
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
project: ['./tsconfig.json'],
ecmaFeatures: {
jsx: true,
},
},
ignorePatterns: ['dist', '.eslintrc.*'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended-type-checked',
'plugin:@typescript-eslint/strict-type-checked',
'plugin:@typescript-eslint/stylistic-type-checked',
'plugin:react-hooks/recommended',
'plugin:react/recommended',
'plugin:jsx-a11y/recommended',
'plugin:import/typescript',
],
plugins: [
'@typescript-eslint',
'react',
'simple-import-sort',
'prettier',
Expand All @@ -32,12 +38,18 @@ module.exports = {
},
},
rules: {
// Format
'prettier/prettier': 'warn',

// React
'react/prop-types': 'off',
'react/jsx-uses-react': 'off',
'react/react-in-jsx-scope': 'off',

// Import
'sort-imports': 'off',
'import/order': 'off',
'import/no-cycle': 'error',
'simple-import-sort/exports': 'warn',
'simple-import-sort/imports': [
'warn',
Expand All @@ -50,36 +62,30 @@ module.exports = {
],
},
],
'import/no-cycle': 'error',
},
overrides: [
// Typescript related rules
{
files: ['*.ts', '*.tsx'],
plugins: ['@typescript-eslint/eslint-plugin'],
extends: ['plugin:@typescript-eslint/recommended'],
parserOptions: {
project: ['./tsconfig.json'],
},

rules: {
// We should absolutely avoid using ts-ignore, but it"s not always possible.
// particular when a dependencies types are incorrect.
'@typescript-eslint/ban-ts-comment': [
'warn',
{ 'ts-ignore': 'allow-with-description' },
],
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-unused-vars': [
'warn',
{
argsIgnorePattern: '^_',
ignoreRestSiblings: true,
},
],
// We should absolutely avoid using ts-ignore, but it"s not always possible.
// particular when a dependencies types are incorrect.
'@typescript-eslint/ban-ts-comment': [
'warn',
{ 'ts-ignore': 'allow-with-description' },
],

// Allow unused variables that start with an underscore.
'@typescript-eslint/no-unused-vars': [
'warn',
{
argsIgnorePattern: '^_',
ignoreRestSiblings: true,
},
},
],

// Disable some TypeScript rules
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/consistent-type-definitions': 'off',
'@typescript-eslint/no-unnecessary-condition': 'off',
'@typescript-eslint/prefer-ts-expect-error': 'off',
},
overrides: [
// Specials rules for testing
{
extends: ['plugin:vitest/recommended'],
Expand Down
4 changes: 3 additions & 1 deletion packages/usehooks-ts/src/useBoolean/useBoolean.demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ export default function Component() {
const { value, setValue, setTrue, setFalse, toggle } = useBoolean(false)

// Just an example to use "setValue"
const customToggle = () => setValue((x: boolean) => !x)
const customToggle = () => {
setValue((x: boolean) => !x)
}

return (
<>
Expand Down
14 changes: 11 additions & 3 deletions packages/usehooks-ts/src/useBoolean/useBoolean.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,17 @@ interface UseBooleanOutput {
export function useBoolean(defaultValue?: boolean): UseBooleanOutput {
const [value, setValue] = useState(!!defaultValue)

const setTrue = useCallback(() => setValue(true), [])
const setFalse = useCallback(() => setValue(false), [])
const toggle = useCallback(() => setValue(x => !x), [])
const setTrue = useCallback(() => {
setValue(true)
}, [])

const setFalse = useCallback(() => {
setValue(false)
}, [])

const toggle = useCallback(() => {
setValue(x => !x)
}, [])

return { value, setValue, setTrue, setFalse, toggle }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,24 @@ import { useCopyToClipboard } from './useCopyToClipboard'

export default function Component() {
const [copiedText, copy] = useCopyToClipboard()

const handleCopy = (text: string) => () => {
copy(text)
.then(() => {
console.log('Copied!', { text })
})
.catch(error => {
console.error('Failed to copy!', error)
})
}

return (
<>
<h1>Click to copy:</h1>
<div style={{ display: 'flex' }}>
<button onClick={() => copy('A')}>A</button>
<button onClick={() => copy('B')}>B</button>
<button onClick={() => copy('C')}>C</button>
<button onClick={handleCopy('A')}>A</button>
<button onClick={handleCopy('B')}>B</button>
<button onClick={handleCopy('C')}>C</button>
</div>
<p>Copied value: {copiedText ?? 'Nothing is copied yet!'}</p>
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { useCallback, useState } from 'react'

/**
* The copied text as `string` or `null` if nothing has been copied yet.
*/
type CopiedValue = string | null
type CopyFn = (text: string) => Promise<boolean> // Return success

/**
* Function to copy text to the clipboard.
* @param text The text to copy to the clipboard.
* @returns {Promise<boolean>} A promise that resolves to `true` if the text was copied successfully, or `false` otherwise.
*/
type CopyFn = (text: string) => Promise<boolean>

/**
* Custom hook for copying text to the clipboard.
Expand Down
2 changes: 1 addition & 1 deletion packages/usehooks-ts/src/useCountdown/useCountdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export function useCountdown(
intervalMs = countdownOptions.interval
isIncrement = countdownOptions.isIncrement
} else {
// eslint-disable-next-line @typescript-eslint/no-extra-semi
// eslint-disable-next-line @typescript-eslint/no-extra-semi, no-extra-semi
;({ countStart, intervalMs, isIncrement, countStop } = countdownOptions)
}

Expand Down
4 changes: 3 additions & 1 deletion packages/usehooks-ts/src/useCounter/useCounter.demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { useCounter } from './useCounter'
export default function Component() {
const { count, setCount, increment, decrement, reset } = useCounter(0)

const multiplyBy2 = () => setCount((x: number) => x * 2)
const multiplyBy2 = () => {
setCount((x: number) => x * 2)
}

return (
<>
Expand Down
14 changes: 10 additions & 4 deletions packages/usehooks-ts/src/useCounter/useCounter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,17 @@ interface UseCounterOutput {
* setCount(10); // Sets count to 10
*/
export function useCounter(initialValue?: number): UseCounterOutput {
const [count, setCount] = useState(initialValue || 0)
const [count, setCount] = useState(initialValue ?? 0)

const increment = () => setCount(x => x + 1)
const decrement = () => setCount(x => x - 1)
const reset = () => setCount(initialValue || 0)
const increment = () => {
setCount(x => x + 1)
}
const decrement = () => {
setCount(x => x - 1)
}
const reset = () => {
setCount(initialValue ?? 0)
}

return {
count,
Expand Down
16 changes: 12 additions & 4 deletions packages/usehooks-ts/src/useDarkMode/useDarkMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,17 @@ export function useDarkMode(

return {
isDarkMode,
toggle: () => setDarkMode(prev => !prev),
enable: () => setDarkMode(true),
disable: () => setDarkMode(false),
set: value => setDarkMode(value),
toggle: () => {
setDarkMode(prev => !prev)
},
enable: () => {
setDarkMode(true)
},
disable: () => {
setDarkMode(false)
},
set: value => {
setDarkMode(value)
},
}
}
4 changes: 3 additions & 1 deletion packages/usehooks-ts/src/useDebounce/useDebounce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ export function useDebounce<T>(value: T, delay?: number): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value)

useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay || 500)
const timer = setTimeout(() => {
setDebouncedValue(value)
}, delay ?? 500)

return () => {
clearTimeout(timer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import { useDebounceCallback } from './useDebounceCallback'
export default function Component() {
const [value, setValue] = useState('')

const debounced = useDebounceCallback(value => {
setValue(value)
}, 500)
const debounced = useDebounceCallback(setValue, 500)

return (
<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ describe('useDebounceCallback()', () => {
expect(debouncedCallback).toHaveBeenCalledTimes(1)
})

it('should debounce the callback function', async () => {
it('should debounce the callback function', () => {
const callback = vitest.fn()
const { result } = renderHook(() => useDebounceCallback(callback, 100))

Expand All @@ -66,7 +66,7 @@ describe('useDebounceCallback()', () => {
expect(callback).toBeCalledWith('test3')
})

it('should cancel the debounced callback', async () => {
it('should cancel the debounced callback', () => {
const delay = 500
const debouncedCallback = vitest.fn()
const { result } = renderHook(() =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('useDebounceValue()', () => {
expect(result.current[0]).toBe('update3')
})

it('should handle options', async () => {
it('should handle options', () => {
const delay = 500
const { result } = renderHook(() =>
useDebounceValue('initial', delay, { leading: true }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function useDebounceValue<T>(
equalityFn?: (left: T, right: T) => boolean
},
): [T, DebouncedState<(value: T) => void>] {
const eq = options?.equalityFn || ((left: T, right: T) => left === right)
const eq = options?.equalityFn ?? ((left: T, right: T) => left === right)
const unwrappedInitialValue =
initialValue instanceof Function ? initialValue() : initialValue
const [debouncedValue, setDebouncedValue] = useState<T>(unwrappedInitialValue)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { useDocumentTitle } from './useDocumentTitle'

describe('useDocumentTitle()', () => {
it('title should be in the document', () => {
renderHook(() => useDocumentTitle('foo'))
renderHook(() => {
useDocumentTitle('foo')
})
expect(window.document.title).toEqual('foo')
})
})
Loading

0 comments on commit eed729f

Please sign in to comment.