diff --git a/frontend/__tests__/unit/components/AnimatedCounter.test.tsx b/frontend/__tests__/unit/components/AnimatedCounter.test.tsx
index 1f1a1e421f..b12061aa03 100644
--- a/frontend/__tests__/unit/components/AnimatedCounter.test.tsx
+++ b/frontend/__tests__/unit/components/AnimatedCounter.test.tsx
@@ -71,7 +71,7 @@ describe('AnimatedCounter', () => {
})
// Should show intermediate value
- const displayedValue = parseInt(screen.getByText(/\d+/).textContent || '0')
+ const displayedValue = Number.parseInt(screen.getByText(/\d+/).textContent || '0')
expect(displayedValue).toBeGreaterThan(0)
expect(displayedValue).toBeLessThanOrEqual(50)
})
diff --git a/frontend/__tests__/unit/components/SearchPageLayout.test.tsx b/frontend/__tests__/unit/components/SearchPageLayout.test.tsx
index 958a480535..fa9889a64f 100644
--- a/frontend/__tests__/unit/components/SearchPageLayout.test.tsx
+++ b/frontend/__tests__/unit/components/SearchPageLayout.test.tsx
@@ -251,7 +251,7 @@ describe('', () => {
{}}
onPageChange={handlePageChange}
searchQuery=""
@@ -346,7 +346,7 @@ describe('', () => {
render(
{}}
onPageChange={() => {}}
diff --git a/frontend/eslint-rules/no-global-isfinite.mjs b/frontend/eslint-rules/no-global-isfinite.mjs
new file mode 100644
index 0000000000..86c186a4bf
--- /dev/null
+++ b/frontend/eslint-rules/no-global-isfinite.mjs
@@ -0,0 +1,31 @@
+const noGlobalIsFiniteRule = {
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description: 'Disallow use of global isFinite, use Number.isFinite instead',
+ recommended: false,
+ },
+ fixable: 'code',
+ messages: {
+ useNumberIsFinite: 'Use Number.isFinite() instead of global isFinite().',
+ },
+ schema: [],
+ },
+ create(context) {
+ return {
+ CallExpression(node) {
+ if (node.callee.type === 'Identifier' && node.callee.name === 'isFinite') {
+ context.report({
+ node: node.callee,
+ messageId: 'useNumberIsFinite',
+ fix(fixer) {
+ return fixer.replaceText(node.callee, 'Number.isFinite')
+ },
+ })
+ }
+ },
+ }
+ },
+}
+
+export default noGlobalIsFiniteRule
diff --git a/frontend/eslint-rules/no-global-isnan.mjs b/frontend/eslint-rules/no-global-isnan.mjs
new file mode 100644
index 0000000000..123f1ee758
--- /dev/null
+++ b/frontend/eslint-rules/no-global-isnan.mjs
@@ -0,0 +1,31 @@
+const noGlobalIsNaNRule = {
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description: 'Disallow use of global isNaN, use Number.isNaN instead',
+ recommended: false,
+ },
+ fixable: 'code',
+ messages: {
+ useNumberIsNaN: 'Use Number.isNaN() instead of global isNaN().',
+ },
+ schema: [],
+ },
+ create(context) {
+ return {
+ CallExpression(node) {
+ if (node.callee.type === 'Identifier' && node.callee.name === 'isNaN') {
+ context.report({
+ node: node.callee,
+ messageId: 'useNumberIsNaN',
+ fix(fixer) {
+ return fixer.replaceText(node.callee, 'Number.isNaN')
+ },
+ })
+ }
+ },
+ }
+ },
+}
+
+export default noGlobalIsNaNRule
diff --git a/frontend/eslint-rules/no-global-nan.mjs b/frontend/eslint-rules/no-global-nan.mjs
new file mode 100644
index 0000000000..0a499b8faa
--- /dev/null
+++ b/frontend/eslint-rules/no-global-nan.mjs
@@ -0,0 +1,44 @@
+const noGlobalNaNRule = {
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description: 'Disallow use of global NaN, use Number.NaN instead',
+ recommended: false,
+ },
+ fixable: 'code',
+ messages: {
+ useNumberNaN: 'Use Number.NaN instead of global NaN.',
+ },
+ schema: [],
+ },
+ create(context) {
+ return {
+ Identifier(node) {
+ // Only report if it's 'NaN' and not already part of 'Number.NaN'
+ if (node.name === 'NaN') {
+ const parent = node.parent
+ if (
+ parent &&
+ parent.type === 'MemberExpression' &&
+ parent.property === node &&
+ parent.object &&
+ parent.object.type === 'Identifier' &&
+ parent.object.name === 'Number'
+ ) {
+ return
+ }
+
+ context.report({
+ node,
+ messageId: 'useNumberNaN',
+ fix(fixer) {
+ return fixer.replaceText(node, 'Number.NaN')
+ },
+ })
+ }
+ },
+ }
+ },
+}
+
+export default noGlobalNaNRule
diff --git a/frontend/eslint-rules/no-global-parsefloat.mjs b/frontend/eslint-rules/no-global-parsefloat.mjs
new file mode 100644
index 0000000000..8e5dbd234a
--- /dev/null
+++ b/frontend/eslint-rules/no-global-parsefloat.mjs
@@ -0,0 +1,31 @@
+const noGlobalParseFloatRule = {
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description: 'Disallow use of global parseFloat, use Number.parseFloat instead',
+ recommended: false,
+ },
+ fixable: 'code',
+ messages: {
+ useNumberParseFloat: 'Use Number.parseFloat() instead of global parseFloat().',
+ },
+ schema: [],
+ },
+ create(context) {
+ return {
+ CallExpression(node) {
+ if (node.callee.type === 'Identifier' && node.callee.name === 'parseFloat') {
+ context.report({
+ node: node.callee,
+ messageId: 'useNumberParseFloat',
+ fix(fixer) {
+ return fixer.replaceText(node.callee, 'Number.parseFloat')
+ },
+ })
+ }
+ },
+ }
+ },
+}
+
+export default noGlobalParseFloatRule
diff --git a/frontend/eslint-rules/no-global-parseint.mjs b/frontend/eslint-rules/no-global-parseint.mjs
new file mode 100644
index 0000000000..75d26028c3
--- /dev/null
+++ b/frontend/eslint-rules/no-global-parseint.mjs
@@ -0,0 +1,31 @@
+const noGlobalParseIntRule = {
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description: 'Disallow use of global parseInt, use Number.parseInt instead',
+ recommended: false,
+ },
+ fixable: 'code',
+ messages: {
+ useNumberParseInt: 'Use Number.parseInt() instead of global parseInt().',
+ },
+ schema: [],
+ },
+ create(context) {
+ return {
+ CallExpression(node) {
+ if (node.callee.type === 'Identifier' && node.callee.name === 'parseInt') {
+ context.report({
+ node: node.callee,
+ messageId: 'useNumberParseInt',
+ fix(fixer) {
+ return fixer.replaceText(node.callee, 'Number.parseInt')
+ },
+ })
+ }
+ },
+ }
+ },
+}
+
+export default noGlobalParseIntRule
diff --git a/frontend/eslint.config.mjs b/frontend/eslint.config.mjs
index a5045824dd..edf4c88d8b 100644
--- a/frontend/eslint.config.mjs
+++ b/frontend/eslint.config.mjs
@@ -15,6 +15,11 @@ import react from 'eslint-plugin-react'
import reactHooks from 'eslint-plugin-react-hooks'
import nextPlugin from '@next/eslint-plugin-next'
import globals from 'globals'
+import noGlobalIsFiniteRule from './eslint-rules/no-global-isfinite.mjs'
+import noGlobalIsNaNRule from './eslint-rules/no-global-isnan.mjs'
+import noGlobalNaNRule from './eslint-rules/no-global-nan.mjs'
+import noGlobalParseFloatRule from './eslint-rules/no-global-parsefloat.mjs'
+import noGlobalParseIntRule from './eslint-rules/no-global-parseint.mjs'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
@@ -61,6 +66,15 @@ const eslintConfig = [
react,
'jsx-a11y': jsxA11y,
'@next/next': nextPlugin,
+ nest: {
+ rules: {
+ 'no-global-isfinite': noGlobalIsFiniteRule,
+ 'no-global-isnan': noGlobalIsNaNRule,
+ 'no-global-nan': noGlobalNaNRule,
+ 'no-global-parsefloat': noGlobalParseFloatRule,
+ 'no-global-parseint': noGlobalParseIntRule,
+ },
+ },
},
settings: {
'import/resolver': {
@@ -149,6 +163,11 @@ const eslintConfig = [
'jsx-a11y/no-distracting-elements': 'warn',
'jsx-a11y/label-has-associated-control': 'error',
'jsx-a11y/click-events-have-key-events': 'warn',
+ 'nest/no-global-isfinite': 'error',
+ 'nest/no-global-isnan': 'error',
+ 'nest/no-global-nan': 'error',
+ 'nest/no-global-parsefloat': 'error',
+ 'nest/no-global-parseint': 'error',
},
},
{
diff --git a/frontend/src/app/my/mentorship/page.tsx b/frontend/src/app/my/mentorship/page.tsx
index 3de19c9c32..e8271be1ad 100644
--- a/frontend/src/app/my/mentorship/page.tsx
+++ b/frontend/src/app/my/mentorship/page.tsx
@@ -24,7 +24,7 @@ const MyMentorshipPage: React.FC = () => {
const username = (session as ExtendedSession)?.user?.login
const initialQuery = searchParams.get('q') || ''
- const initialPage = parseInt(searchParams.get('page') || '1', 10)
+ const initialPage = Number.parseInt(searchParams.get('page') || '1', 10)
const [searchQuery, setSearchQuery] = useState(initialQuery)
const [debouncedQuery, setDebouncedQuery] = useState(initialQuery)
diff --git a/frontend/src/components/ModuleCard.tsx b/frontend/src/components/ModuleCard.tsx
index 479e9ec136..4f70839e6c 100644
--- a/frontend/src/components/ModuleCard.tsx
+++ b/frontend/src/components/ModuleCard.tsx
@@ -98,7 +98,7 @@ export default ModuleCard
export const getSimpleDuration = (start: string, end: string): string => {
const startDate = new Date(start)
const endDate = new Date(end)
- if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
+ if (Number.isNaN(startDate.getTime()) || Number.isNaN(endDate.getTime())) {
return 'Invalid duration'
}
diff --git a/frontend/src/hooks/useSearchPage.ts b/frontend/src/hooks/useSearchPage.ts
index dd92aa9545..7846ea3684 100644
--- a/frontend/src/hooks/useSearchPage.ts
+++ b/frontend/src/hooks/useSearchPage.ts
@@ -37,7 +37,9 @@ export function useSearchPage({
const searchParams = useSearchParams()
const [items, setItems] = useState([])
- const [currentPage, setCurrentPage] = useState(parseInt(searchParams.get('page') || '1'))
+ const [currentPage, setCurrentPage] = useState(
+ Number.parseInt(searchParams.get('page') || '1')
+ )
const [searchQuery, setSearchQuery] = useState(searchParams.get('q') || '')
const [sortBy, setSortBy] = useState(searchParams.get('sortBy') || defaultSortBy)
const [order, setOrder] = useState(searchParams.get('order') || defaultOrder)
diff --git a/frontend/src/utils/dateFormatter.ts b/frontend/src/utils/dateFormatter.ts
index 26b6e0a170..3c6ab04f36 100644
--- a/frontend/src/utils/dateFormatter.ts
+++ b/frontend/src/utils/dateFormatter.ts
@@ -8,7 +8,7 @@ export const formatDate = (input: number | string) => {
? new Date(input * 1000) // Unix timestamp in seconds
: new Date(input) // ISO date string
- if (isNaN(date.getTime())) {
+ if (Number.isNaN(date.getTime())) {
throw new Error('Invalid date')
}
@@ -23,7 +23,7 @@ export const formatDateRange = (startDate: number | string, endDate: number | st
const start = typeof startDate === 'number' ? new Date(startDate * 1000) : new Date(startDate)
const end = typeof endDate === 'number' ? new Date(endDate * 1000) : new Date(endDate)
- if (isNaN(start.getTime()) || isNaN(end.getTime())) {
+ if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) {
throw new Error('Invalid date')
}
@@ -63,7 +63,7 @@ export const formatDateRange = (startDate: number | string, endDate: number | st
export const formatDateForInput = (dateStr: string) => {
if (!dateStr) return ''
const date = new Date(dateStr)
- if (isNaN(date.getTime())) {
+ if (Number.isNaN(date.getTime())) {
throw new Error('Invalid date')
}
return date.toISOString().slice(0, 10)
diff --git a/frontend/src/utils/round.ts b/frontend/src/utils/round.ts
index 7440658196..8b41f06654 100644
--- a/frontend/src/utils/round.ts
+++ b/frontend/src/utils/round.ts
@@ -1,5 +1,5 @@
export const round = (value: number, precision = 2): number => {
- if (isNaN(value)) return 0 // Handle NaN values
+ if (Number.isNaN(value)) return 0 // Handle NaN values
if (precision < 0) {
throw new Error('Precision must be a non-negative integer')
}