Skip to content

Commit

Permalink
[v4] setup @typescript-eslint/no-unnecessary-condition rule and fix…
Browse files Browse the repository at this point in the history
… warnings (#3747)

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* more

* fix

* aa

* more

* more

* more

* fix typecheck
  • Loading branch information
dimaMachina authored Nov 29, 2024
1 parent dcd4174 commit fbeef15
Show file tree
Hide file tree
Showing 50 changed files with 181 additions and 142 deletions.
7 changes: 7 additions & 0 deletions .changeset/blue-crabs-deny.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"nextra-theme-blog": patch
"nextra-theme-docs": patch
"nextra": patch
---

setup `@typescript-eslint/no-unnecessary-condition` rule and fix warnings
2 changes: 1 addition & 1 deletion docs/app/docs/built-ins/head/_slider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const Slider: FC = ({
}

function hexToRgb(hex: `#${string}`): string {
const bigint = parseInt(hex.slice(1), 16)
const bigint = Number.parseInt(hex.slice(1), 16)
const r = (bigint >> 16) & 255
const g = (bigint >> 8) & 255
const b = bigint & 255
Expand Down
2 changes: 1 addition & 1 deletion docs/app/og/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ImageResponse } from 'next/og'

export const runtime = 'edge'

const font = fetch(new URL('./Inter-SemiBold.otf', import.meta.url)).then(res =>
const font = fetch(new URL('Inter-SemiBold.otf', import.meta.url)).then(res =>
res.arrayBuffer()
)

Expand Down
3 changes: 2 additions & 1 deletion docs/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"name": "next"
}
],
"strictNullChecks": true
"strictNullChecks": true,
"noUncheckedIndexedAccess": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules", ".next"]
Expand Down
3 changes: 2 additions & 1 deletion examples/blog/app/posts/get-posts.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export async function getPosts() {
}

export async function getTags() {
const tags = (await getPosts()).flatMap(post => post.frontMatter.tags)
const posts = await getPosts()
const tags = posts.flatMap(post => post.frontMatter.tags)
return tags
}
6 changes: 4 additions & 2 deletions examples/blog/app/posts/page.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ export const metadata = {
}

export default async function PostsPage() {
const allTags = (await getTags()).reduce((acc, curr) => {
const tags = await getTags()
const posts = await getPosts()
const allTags = tags.reduce((acc, curr) => {
acc[curr] ??= 0
acc[curr] += 1
return acc
Expand All @@ -26,7 +28,7 @@ export default async function PostsPage() {
</Link>
))}
</div>
{(await getPosts()).map(post => (
{posts.map(post => (
<PostCard key={post.route} post={post} />
))}
</div>
Expand Down
6 changes: 4 additions & 2 deletions examples/blog/app/tags/[tag]/page.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ export async function generateStaticParams() {

export default async function TagPage(props) {
const params = await props.params
const { title } = await generateMetadata({ params })
const posts = await getPosts()
return (
<>
<h1>{(await generateMetadata({ params })).title}</h1>
{(await getPosts())
<h1>{title}</h1>
{posts
.filter(post =>
post.frontMatter.tags.includes(decodeURIComponent(params.tag))
)
Expand Down
11 changes: 2 additions & 9 deletions examples/swr-site/app/_dictionaries/get-dictionary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const dictionaries: Dictionaries = {
ru: () => import('./ru')
}

export async function getDictionary(locale: Locale): Promise<Dictionary> {
export async function getDictionary(locale: string): Promise<Dictionary> {
const { default: dictionary } = await (
dictionaries[locale] || dictionaries.en
)()
Expand All @@ -18,12 +18,5 @@ export async function getDictionary(locale: Locale): Promise<Dictionary> {
}

export function getDirection(locale: Locale): 'ltr' | 'rtl' {
switch (locale) {
case 'es':
return 'rtl'
case 'en':
case 'ru':
default:
return 'ltr'
}
return locale === 'es' ? 'rtl' : 'ltr'
}
3 changes: 2 additions & 1 deletion examples/swr-site/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"name": "next"
}
],
"strictNullChecks": true
"strictNullChecks": true,
"noUncheckedIndexedAccess": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules", ".next"]
Expand Down
1 change: 1 addition & 0 deletions packages/esbuild-react-compiler-plugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const reactCompilerPlugin = (
relativePath,
'was not optimized with react-compiler'
)
console.log(result)
}

resolve({ contents: result, loader })
Expand Down
3 changes: 2 additions & 1 deletion packages/esbuild-react-compiler-plugin/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"strictNullChecks": true,
"lib": ["esnext", "dom"],
"moduleResolution": "node",
"resolveJsonModule": true
"resolveJsonModule": true,
"noUncheckedIndexedAccess": true
},
"exclude": ["dist"]
}
32 changes: 18 additions & 14 deletions packages/eslint-config/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ const config: Config = tseslint.config(
extends: [
js.configs.recommended,
tseslint.configs.recommended,
eslintPluginUnicorn.configs['flat/recommended'],
eslintConfigPrettier
],
plugins: {
import: eslintPluginImport,
unicorn: eslintPluginUnicorn,
sonarjs: eslintPluginSonarJs
},
rules: {
Expand All @@ -68,13 +68,8 @@ const config: Config = tseslint.config(
{ VariableDeclarator: { object: true } }
],
'import/no-duplicates': 'error',
'no-negated-condition': 'off',
'unicorn/no-negated-condition': 'error',
'prefer-regex-literals': ['error', { disallowRedundantWrapping: true }],
'object-shorthand': ['error', 'always'],
'unicorn/prefer-regexp-test': 'error',
'unicorn/no-array-for-each': 'error',
'unicorn/prefer-string-replace-all': 'error',
'@typescript-eslint/prefer-for-of': 'error',
quotes: ['error', 'single', { avoidEscape: true }], // Matches Prettier, but also replaces backticks
'@typescript-eslint/no-unused-vars': [
Expand All @@ -86,21 +81,29 @@ const config: Config = tseslint.config(
],
'prefer-object-spread': 'error',
'prefer-arrow-callback': ['error', { allowNamedFunctions: true }],
'unicorn/prefer-at': 'error',
'sonarjs/no-small-switch': 'error',
'prefer-const': ['error', { destructuring: 'all' }],
'unicorn/prefer-array-index-of': 'error',
'sonarjs/no-unused-collection': 'error',
'unicorn/catch-error-name': 'error',
'unicorn/prefer-optional-catch-binding': 'error',
'unicorn/filename-case': 'error',
eqeqeq: ['error', 'always', { null: 'ignore' }],
'unicorn/prefer-node-protocol': 'error',
'unicorn/switch-case-braces': ['error', 'avoid'],
// todo: enable
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/ban-ts-comment': 'off'
'@typescript-eslint/ban-ts-comment': 'off',

'unicorn/no-hex-escape': 'off', // todo
'unicorn/escape-case': 'off', // todo
'unicorn/consistent-function-scoping': 'off', // todo
'unicorn/prefer-module': 'off',
'unicorn/no-array-reduce': 'off',
'unicorn/prefer-top-level-await': 'off', // Check if possible to refactor without breaking

'unicorn/prevent-abbreviations': 'off', // Too many cases
'unicorn/explicit-length-check': 'off', // I don't like
'unicorn/no-null': 'off', // I don't like
'unicorn/prefer-global-this': 'off', // Bundlers are smarter with window
'unicorn/prefer-optional-catch-binding': 'off' // catch by @typescript-eslint/no-unused-vars
}
},
// Rules for React files
Expand Down Expand Up @@ -174,7 +177,8 @@ const config: Config = tseslint.config(
'@typescript-eslint/prefer-destructuring': [
'error',
{ VariableDeclarator: { object: true } }
]
],
'@typescript-eslint/no-unnecessary-condition': 'error'
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-config/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"strictNullChecks": true,
"lib": ["esnext", "dom"],
"moduleResolution": "node",
"resolveJsonModule": true
"noUncheckedIndexedAccess": true
},
"exclude": ["dist"]
}
9 changes: 6 additions & 3 deletions packages/nextra-theme-blog/src/components/meta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@ export const Meta: FC<BlogMetadata & { children: ReactNode }> = ({

{children}

{(author || date) && (readingTime || tags?.length) && (
<span className="x:px-1"></span>
)}
{
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- fixme
(author || date) && (readingTime || tags?.length) && (
<span className="x:px-1"></span>
)
}
{readingTimeText || tagsEl}
</div>
{readingTime && (
Expand Down
7 changes: 5 additions & 2 deletions packages/nextra-theme-blog/src/mdx-components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,11 @@ export const useMDXComponents = ({
<Meta {...(metadata as BlogMetadata)}>
{dateObj && (
<time dateTime={dateObj.toISOString()}>
{(DateFormatter && <DateFormatter date={dateObj} />) ||
dateObj.toLocaleDateString()}
{DateFormatter ? (
<DateFormatter date={dateObj} />
) : (
dateObj.toLocaleDateString()
)}
</time>
)}
</Meta>
Expand Down
3 changes: 2 additions & 1 deletion packages/nextra-theme-blog/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"strictNullChecks": true,
"jsx": "react-jsx",
"moduleResolution": "bundler",
"types": ["vitest/globals"]
"types": ["vitest/globals"],
"noUncheckedIndexedAccess": true
},
"exclude": ["dist"]
}
4 changes: 2 additions & 2 deletions packages/nextra-theme-docs/src/components/locale-switch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { GlobeIcon } from 'nextra/icons'
import type { FC } from 'react'
import { useThemeConfig } from '../stores'

const ONE_YEAR = 365 * 24 * 60 * 60 * 1_000
const ONE_YEAR = 365 * 24 * 60 * 60 * 1000

interface LocaleSwitchProps {
lite?: boolean
Expand All @@ -29,7 +29,7 @@ export const LocaleSwitch: FC<LocaleSwitchProps> = ({ lite, className }) => {
document.cookie = `NEXT_LOCALE=${lang}; expires=${date.toUTCString()}; path=/`
location.href = addBasePath(pathname.replace(`/${locale}`, `/${lang}`))
}}
value={locale}
value={locale!}
selectedOption={
<span className="x:flex x:items-center x:gap-2">
<GlobeIcon height="12" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ const NavbarMenu: FC<{
anchor={{ to: 'top end', gap: 10, padding: 16 }}
>
{Object.entries(
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- fixme
(menu.items as Record<string, { title: string; href?: string }>) || {}
).map(([key, item]) => (
<_MenuItem
Expand Down
49 changes: 25 additions & 24 deletions packages/nextra-theme-docs/src/components/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ type FolderProps = {

const Folder: FC<FolderProps> = ({ item, anchors, onFocus, level }) => {
const routeOriginal = useFSRoute()
const [route] = routeOriginal.split('#', 1)
const route = routeOriginal.split('#', 1)[0]!
const hasRoute = !!item.route // for item.type === 'menu' will be ''
const active = hasRoute && [route, route + '/'].includes(item.route + '/')
const activeRouteInside =
Expand All @@ -99,19 +99,20 @@ const Folder: FC<FolderProps> = ({ item, anchors, onFocus, level }) => {

const [, rerender] = useState<object>()

const handleClick: MouseEventHandler = useCallback(event => {
const el = event.currentTarget
const isClickOnIcon =
el /* will be always <a> or <button> */ !==
event.target /* can be <svg> or <path> */
if (isClickOnIcon) {
event.preventDefault()
}
const isOpen = el.parentElement!.classList.contains('open')
const route = el.getAttribute('href') || el.getAttribute('data-href') || ''
TreeState[route] = !isOpen
rerender({})
}, [])
const handleClick: MouseEventHandler<HTMLAnchorElement | HTMLButtonElement> =
useCallback(event => {
const el = event.currentTarget
const isClickOnIcon =
el /* will be always <a> or <button> */ !==
event.target /* can be <svg> or <path> */
if (isClickOnIcon) {
event.preventDefault()
}
const isOpen = el.parentElement!.classList.contains('open')
const route = el.getAttribute('href') || el.dataset.href || ''
TreeState[route] = !isOpen
rerender({})
}, [])

useEffect(() => {
function updateTreeState() {
Expand Down Expand Up @@ -140,12 +141,14 @@ const Folder: FC<FolderProps> = ({ item, anchors, onFocus, level }) => {
const routes = Object.fromEntries(
(menu.children || []).map(route => [route.name, route])
)
item.children = Object.entries(menu.items || {}).map(([key, item]) => {
return {
...(routes[key] || { name: key /* for React key prop */ }),
...(item as object)
}
})
// @ts-expect-error
item.children = Object.entries(menu.items || {}) // eslint-disable-line @typescript-eslint/no-unnecessary-condition -- fixme
.map(([key, item]) => {
return {
...(routes[key] || { name: key /* for React key prop */ }),
...(item as object)
}
})
}

const isLink = 'frontMatter' in item
Expand Down Expand Up @@ -265,11 +268,9 @@ interface MenuProps {
level: number
}

const handleFocus: FocusEventHandler = event => {
const handleFocus: FocusEventHandler<HTMLAnchorElement> = event => {
const route =
event.target.getAttribute('href') ||
event.target.getAttribute('data-href') ||
''
event.target.getAttribute('href') || event.target.dataset.href || ''
setFocusedRoute(route)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ export const ClientWrapper: MDXWrapper = ({ toc, children, metadata }) => {
} = useConfig().normalizePagesResult
const themeConfig = useThemeConfig()

const date =
themeContext.timestamp && themeConfig.lastUpdated && metadata.timestamp
const date = themeContext.timestamp && metadata.timestamp

// We can't update store in server component so doing it in client component
useEffect(() => {
Expand Down
3 changes: 2 additions & 1 deletion packages/nextra-theme-docs/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"jsx": "react-jsx",
"moduleResolution": "bundler",
"lib": ["ESNext", "DOM"],
"types": ["vitest/globals"]
"types": ["vitest/globals"],
"noUncheckedIndexedAccess": true
},
"exclude": ["dist"]
}
2 changes: 1 addition & 1 deletion packages/nextra/loader.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* @param {string} code
* @return {Promise<void>}
*/
module.exports = async function (code) {
module.exports = async function loader(code) {
const callback = this.async()

try {
Expand Down
Loading

0 comments on commit fbeef15

Please sign in to comment.