diff --git a/.eslintrc.js b/.eslintrc.js index 4cb7db96c4c..546ab68fe35 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -32,6 +32,14 @@ module.exports = { 'react/jsx-filename-extension': ['error', { extensions: ['.jsx', '.tsx', '.mdx'] }], 'react/react-in-jsx-scope': 'off', + // Disallow console statements except in specific files + 'no-console': [ + 'error', + { + allow: ['warn', 'info'], + }, + ], + // We utilize prop spreading 'react/jsx-props-no-spreading': 'off', diff --git a/apps/web/app/(base-org)/jobs/page.tsx b/apps/web/app/(base-org)/jobs/page.tsx index 79db1106976..fa0064cd2e2 100644 --- a/apps/web/app/(base-org)/jobs/page.tsx +++ b/apps/web/app/(base-org)/jobs/page.tsx @@ -6,6 +6,7 @@ import JobsList from 'apps/web/src/components/Jobs/JobsList'; import { Hero } from 'apps/web/src/components/Jobs/Redesign/Hero'; import { WebGLCanvas } from 'apps/web/src/components/WebGL/WebGLCanvas'; import { greenhouseApiUrl } from 'apps/web/src/constants'; +import { logger } from 'apps/web/src/utils/logger'; import type { Metadata } from 'next'; export const metadata: Metadata = { @@ -25,7 +26,7 @@ async function getJobs() { const { jobs } = (await res.json()) as { jobs: JobType[] }; return jobs; } catch (_error) { - console.error(_error); + logger.error('Error fetching jobs', _error); } return []; } diff --git a/apps/web/app/(basenames)/api/proxy/route.ts b/apps/web/app/(basenames)/api/proxy/route.ts index 03cde02dad1..0028395e7bb 100644 --- a/apps/web/app/(basenames)/api/proxy/route.ts +++ b/apps/web/app/(basenames)/api/proxy/route.ts @@ -1,5 +1,6 @@ import { NextRequest, NextResponse } from 'next/server'; import { isAddress } from 'viem'; +import { logger } from 'apps/web/src/utils/logger'; const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY; const TALENT_PROTOCOL_API_KEY = process.env.TALENT_PROTOCOL_API_KEY; @@ -60,7 +61,7 @@ export async function GET(req: NextRequest) { return NextResponse.json({ error: responseData }, { status: externalResponse.status }); } } catch (error) { - console.error('Error in API proxy:', error); + logger.error('Error in API proxy', error); return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); } } diff --git a/apps/web/app/api/auth/register/route.ts b/apps/web/app/api/auth/register/route.ts index 6357b4c08f6..210381953a0 100644 --- a/apps/web/app/api/auth/register/route.ts +++ b/apps/web/app/api/auth/register/route.ts @@ -1,4 +1,5 @@ import { NextRequest, NextResponse } from 'next/server'; +import { logger } from 'apps/web/src/utils/logger'; type RegistrationBody = { username: string; @@ -57,7 +58,7 @@ export async function POST(request: NextRequest) { // For now, we'll just simulate a successful registration // This is a placeholder implementation - console.log('User registration attempt:', { username, email }); + logger.info('User registration attempt', { username, email }); return NextResponse.json( { @@ -70,7 +71,7 @@ export async function POST(request: NextRequest) { { status: 201 } ); } catch (error) { - console.error('Registration error:', error); + logger.error('Registration error', error); return NextResponse.json( { error: 'Internal server error' }, { status: 500 } diff --git a/apps/web/app/farcaster/user/route.ts b/apps/web/app/farcaster/user/route.ts index 27657ceeab2..5b89d3147d4 100644 --- a/apps/web/app/farcaster/user/route.ts +++ b/apps/web/app/farcaster/user/route.ts @@ -1,11 +1,15 @@ import { NextResponse } from 'next/server'; +import { logger } from 'apps/web/src/utils/logger'; const API_URL = 'https://api.neynar.com/v2/farcaster/user/bulk'; const API_KEY = process.env.NEYNAR_API_KEY; -if (!API_KEY) console.error('NEYNAR_API_KEY required'); - export async function GET(request: Request) { + if (!API_KEY) { + logger.error('NEYNAR_API_KEY required', new Error('Missing NEYNAR_API_KEY')); + return NextResponse.json({ error: 'Server configuration error' }, { status: 500 }); + } + const { searchParams } = new URL(request.url); const fids = searchParams.get('fid'); diff --git a/apps/web/contexts/Errors.tsx b/apps/web/contexts/Errors.tsx index 007b5c4d3b2..fb9c0a834d5 100644 --- a/apps/web/contexts/Errors.tsx +++ b/apps/web/contexts/Errors.tsx @@ -40,6 +40,7 @@ export default function ErrorsProvider({ children, context }: ErrorsProviderProp if (isDevelopment) { console.log('\n--------------------------------------'); console.info(`Error caught with message: "${message}"`); + // eslint-disable-next-line no-console console.error(error); console.info(`Context: "${fullContext}"`); console.log('--------------------------------------\n'); diff --git a/apps/web/src/components/Basenames/RegistrationSuccessMessage/index.tsx b/apps/web/src/components/Basenames/RegistrationSuccessMessage/index.tsx index e9328d11009..59362f04b23 100644 --- a/apps/web/src/components/Basenames/RegistrationSuccessMessage/index.tsx +++ b/apps/web/src/components/Basenames/RegistrationSuccessMessage/index.tsx @@ -1,4 +1,5 @@ import { useAnalytics } from 'apps/web/contexts/Analytics'; +import { useErrors } from 'apps/web/contexts/Errors'; import { RegistrationSteps, useRegistration, @@ -18,6 +19,7 @@ export default function RegistrationSuccessMessage() { const { address } = useAccount(); const { logEventWithContext } = useAnalytics(); + const { logError } = useErrors(); const [popupMessage, setPopupMessage] = useState(null); @@ -36,9 +38,9 @@ export default function RegistrationSuccessMessage() { }) .catch((error) => { setPopupMessage(`${error.message}`); - console.error('Error:', error); + logError(error, 'Error claiming USDC'); }); - }, [address]); + }, [address, logError]); const closePopup = useCallback(() => setPopupMessage(null), []); diff --git a/apps/web/src/components/Basenames/UsernameProfileSectionHeatmap/index.tsx b/apps/web/src/components/Basenames/UsernameProfileSectionHeatmap/index.tsx index 25c62cde65d..114c0bcf2da 100644 --- a/apps/web/src/components/Basenames/UsernameProfileSectionHeatmap/index.tsx +++ b/apps/web/src/components/Basenames/UsernameProfileSectionHeatmap/index.tsx @@ -199,18 +199,18 @@ export default function UsernameProfileSectionHeatmap() { return []; // Return an empty array for no transactions } else if (data.status === '0' && data.message === 'Exception') { if (retryCount > 0) { - console.log(`API returned an exception. Retrying... (${retryCount} attempts left)`); + logger.info(`API returned an exception. Retrying... (${retryCount} attempts left)`); await new Promise((resolve) => setTimeout(resolve, 2000)); return await fetchTransactions(apiUrl, retryCount - 1); } else { throw new Error(`API Error: ${data.message}`); } } else { - console.error('Unexpected API response structure:', json); + logger.error('Unexpected API response structure', json); return []; } } catch (e) { - console.error('Error fetching transactions:', e); + logger.error('Error fetching transactions', e); throw e; } }, diff --git a/apps/web/src/components/Basenames/UsernameProfileSidebar/index.tsx b/apps/web/src/components/Basenames/UsernameProfileSidebar/index.tsx index c54e7a6e145..81ba01da151 100644 --- a/apps/web/src/components/Basenames/UsernameProfileSidebar/index.tsx +++ b/apps/web/src/components/Basenames/UsernameProfileSidebar/index.tsx @@ -78,7 +78,9 @@ export default function UsernameProfileSidebar() { const reclaimProfile = useCallback(() => { if (!reclaimContract) return; initiateReclaim(reclaimContract) - .then((result) => console.log({ result })) + .then(() => { + // Successfully reclaimed profile + }) .catch((error) => { logError(error, 'Failed to reclaim profile'); }); diff --git a/apps/web/src/components/Builders/Landing/Hero/SearchModal.tsx b/apps/web/src/components/Builders/Landing/Hero/SearchModal.tsx index 1728b7a2db6..10f942ab83a 100644 --- a/apps/web/src/components/Builders/Landing/Hero/SearchModal.tsx +++ b/apps/web/src/components/Builders/Landing/Hero/SearchModal.tsx @@ -6,6 +6,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import Input from 'apps/web/src/components/Input'; import { createPortal } from 'react-dom'; import Link from 'apps/web/src/components/Link'; +import { logger } from 'apps/web/src/utils/logger'; type SearchCategory = { category: string; @@ -43,12 +44,11 @@ const searchConfig: SearchCategory[] = [ href: '', icon: 'copy', onClick: () => { - console.log('clicked'); const copyCreateOnchain = async () => { try { await navigator.clipboard.writeText('npm create onchain'); } catch (error) { - console.error('Failed to copy to clipboard', error); + logger.error('Failed to copy to clipboard', error); } }; void copyCreateOnchain(); diff --git a/apps/web/src/components/ConnectWalletButton/CustomWalletAdvancedAddressDetails.tsx b/apps/web/src/components/ConnectWalletButton/CustomWalletAdvancedAddressDetails.tsx index a58bd765b26..28d7030a522 100644 --- a/apps/web/src/components/ConnectWalletButton/CustomWalletAdvancedAddressDetails.tsx +++ b/apps/web/src/components/ConnectWalletButton/CustomWalletAdvancedAddressDetails.tsx @@ -4,6 +4,7 @@ import classNames from 'classnames'; import { useCallback, useState } from 'react'; import { useAccount } from 'wagmi'; import { useCopyToClipboard } from 'usehooks-ts'; +import { logger } from 'apps/web/src/utils/logger'; export function CustomWalletAdvancedAddressDetails() { const { address, chain } = useAccount(); @@ -17,7 +18,7 @@ export function CustomWalletAdvancedAddressDetails() { }) .catch((err) => { setCopyText('Failed to copy'); - console.error('Failed to copy address:', err); + logger.error('Failed to copy address', err); }) .finally(() => { setTimeout(() => setCopyText('Copy'), 2000); diff --git a/apps/web/src/components/CopyButton/CopyButton.tsx b/apps/web/src/components/CopyButton/CopyButton.tsx index ffbb8a9a633..decd1408fa9 100644 --- a/apps/web/src/components/CopyButton/CopyButton.tsx +++ b/apps/web/src/components/CopyButton/CopyButton.tsx @@ -2,6 +2,7 @@ import classNames from 'classnames'; import { SVGProps, useCallback, useEffect, useRef, useState } from 'react'; +import { logger } from 'apps/web/src/utils/logger'; const handleCopy = async ( text: string, @@ -22,7 +23,7 @@ const handleCopy = async ( setCopied(false); }, 2000); } catch (err) { - console.error('Failed to copy text:', err); + logger.error('Failed to copy text', err); } }; diff --git a/apps/web/src/components/ImageCloudinary/index.tsx b/apps/web/src/components/ImageCloudinary/index.tsx index 8b0b20e2687..0e322c05c64 100644 --- a/apps/web/src/components/ImageCloudinary/index.tsx +++ b/apps/web/src/components/ImageCloudinary/index.tsx @@ -5,6 +5,7 @@ import { getImageAbsoluteSource, getCloudinaryMediaUrl } from 'apps/web/src/util import { isDataUrl } from 'apps/web/src/utils/urls'; import { StaticImageData } from 'next/image'; import { CSSProperties, useEffect, useState } from 'react'; +import { logger } from 'apps/web/src/utils/logger'; type ImageCloudinaryProps = { src: string | StaticImageData; @@ -74,13 +75,13 @@ export default function ImageCloudinary({ setCloudinaryUploadUrl(url); } } catch (error) { - console.error('Error getting Cloudinary URL:', error); + logger.error('Error getting Cloudinary URL', error); } } handleGetCloudinaryUrl() .then() - .catch((error) => console.log(error)); + .catch((error) => logger.error('Error handling Cloudinary URL', error)); } }, [absoluteSrc, shouldUploadToCloudinary, width]); diff --git a/apps/web/src/components/Layout/Navigation/Sidebar/Logo.tsx b/apps/web/src/components/Layout/Navigation/Sidebar/Logo.tsx index 0d038eae707..cec3d9f1523 100644 --- a/apps/web/src/components/Layout/Navigation/Sidebar/Logo.tsx +++ b/apps/web/src/components/Layout/Navigation/Sidebar/Logo.tsx @@ -7,6 +7,7 @@ import { stagger } from 'motion'; import { AnimationSequence, useAnimate } from 'motion/react'; import Link from 'apps/web/src/components/Link'; import { SVGProps, useRef, useCallback } from 'react'; +import { logger } from 'apps/web/src/utils/logger'; export function SidebarLogo() { const [scope, animate] = useAnimate(); @@ -204,7 +205,7 @@ export function SidebarLogo() { }, 0); } } catch (error) { - console.error(error); + logger.error('Error in logo animation', error); } finally { isAnimating.current = false; } @@ -407,7 +408,7 @@ export function SidebarLogo() { // execute second timeline await animate(secondSequence, { duration: 1.2 }); } catch (error) { - console.error(error); + logger.error('Error in logo mouse out animation', error); } finally { secondTimelineRunning.current = false; firstTimelineCompleted.current = false; diff --git a/apps/web/src/components/WebGL/Scenes/AsciiScene.tsx b/apps/web/src/components/WebGL/Scenes/AsciiScene.tsx index b53ab4a6e04..2d22919999b 100644 --- a/apps/web/src/components/WebGL/Scenes/AsciiScene.tsx +++ b/apps/web/src/components/WebGL/Scenes/AsciiScene.tsx @@ -3,6 +3,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import * as THREE from 'three'; import { WebGLView } from 'apps/web/src/components/WebGL/WebGLView'; +import { logger } from 'apps/web/src/utils/logger'; type AsciiSceneProps = { imagePath?: string; @@ -110,7 +111,7 @@ export default function AsciiScene({ }, undefined, (error) => { - console.error('Failed to load image texture:', error); + logger.error('Failed to load image texture', error); }, ); }, [imagePath]); diff --git a/apps/web/src/components/base-org/root/Redesign/Section/BaseJoin/InteractiveCard.tsx b/apps/web/src/components/base-org/root/Redesign/Section/BaseJoin/InteractiveCard.tsx index 1ceeab1f258..cd5923244f7 100644 --- a/apps/web/src/components/base-org/root/Redesign/Section/BaseJoin/InteractiveCard.tsx +++ b/apps/web/src/components/base-org/root/Redesign/Section/BaseJoin/InteractiveCard.tsx @@ -9,6 +9,7 @@ import { TitleLevel } from 'apps/web/src/components/base-org/typography/TitleRed import Text from 'apps/web/src/components/base-org/typography/TextRedesign'; import { TextVariant } from 'apps/web/src/components/base-org/typography/TextRedesign/types'; import NextImage from 'next/image'; +import { logger } from 'apps/web/src/utils/logger'; type CardProps = { title: string; @@ -174,17 +175,15 @@ export const useImageTexture = (imagePath: string) => { loadedTexture.minFilter = THREE.LinearFilter; setImageTexture(loadedTexture); }, - (progress) => { - console.log('Image texture loading progress:', progress); - }, + undefined, (error) => { - console.error('Failed to load image texture:', error); + logger.error('Failed to load image texture', error); }, ); }; img.onerror = (error) => { - console.error('Failed to load image for dimensions:', error); + logger.error('Failed to load image for dimensions', error); setImageDimensions({ width: 1, height: 1 }); }; diff --git a/apps/web/src/components/base-org/root/Redesign/Vision/Section/Believe/Card/Scene/index.tsx b/apps/web/src/components/base-org/root/Redesign/Vision/Section/Believe/Card/Scene/index.tsx index 012da9cddb4..b3b66ce580d 100644 --- a/apps/web/src/components/base-org/root/Redesign/Vision/Section/Believe/Card/Scene/index.tsx +++ b/apps/web/src/components/base-org/root/Redesign/Vision/Section/Believe/Card/Scene/index.tsx @@ -10,6 +10,7 @@ import { GLTFLoader } from 'three-stdlib'; import { WebGlTunnelIn } from 'apps/web/src/components/WebGL/Tunnel'; import { useWebGLInteraction } from 'apps/web/src/hooks/useWebGLInteraction'; import { Float } from '@react-three/drei'; +import { logger } from 'apps/web/src/utils/logger'; const DEBUG = false; const CAM_SIZE = 1.6; @@ -207,7 +208,7 @@ export function CardScene({ }, undefined, (error) => { - console.error('Failed to load RGB texture:', rgbTexturePath, error); + logger.error('Failed to load RGB texture', error, { rgbTexturePath }); }, ); } else { @@ -237,7 +238,7 @@ export function CardScene({ }, undefined, (error: unknown) => { - console.error('Error loading GLTF model:', gltfSrc, error); + logger.error('Error loading GLTF model', error, { gltfSrc }); }, ); }, [gltfSrc]); diff --git a/apps/web/src/utils/bugsnag.ts b/apps/web/src/utils/bugsnag.ts index 768ca228af4..51a50d76f84 100644 --- a/apps/web/src/utils/bugsnag.ts +++ b/apps/web/src/utils/bugsnag.ts @@ -1,4 +1,5 @@ // import React from 'react'; +/* eslint-disable no-console */ import type { BugsnagPluginReactResult } from '@bugsnag/plugin-react'; import type { OnErrorCallback } from '@bugsnag/core/types/common'; diff --git a/apps/web/src/utils/datastores/kv/index.ts b/apps/web/src/utils/datastores/kv/index.ts index 15e85eaef11..01b03eaff19 100644 --- a/apps/web/src/utils/datastores/kv/index.ts +++ b/apps/web/src/utils/datastores/kv/index.ts @@ -27,10 +27,6 @@ class KVManager { private async getClient(): Promise { if (!this.client) { - console.log( - 'creating new redis client: ', - 'url' in this.connectionArg ? this.connectionArg.url : this.connectionArg.host, - ); if (!this.connectionArg) { throw new Error('No URL or options provided to KVManager'); } @@ -45,20 +41,13 @@ class KVManager { }); } - console.log('redis client created', this.client); - - console.log( - `pinging ${ - 'url' in this.connectionArg ? this.connectionArg.url : this.connectionArg.host - }`, - ); const pingRes = await this.client.ping(); - console.log('ping response', pingRes); + logger.info('Redis client created and connected', { + connection: 'url' in this.connectionArg ? this.connectionArg.url : this.connectionArg.host, + pingResponse: pingRes + }); } catch (err) { - if (!isDevelopment) { - logger.error('KV connection failed', err); - } - console.error(err); + logger.error('KV connection failed', err); throw new Error(`Failed to connect to KV: ${err}`); } } @@ -72,10 +61,7 @@ class KVManager { const pingRes = await this.client.ping(); return pingRes; } catch (err) { - if (!isDevelopment) { - logger.error('Failed to scan keys', err); - } - console.error(err); + logger.error('Failed to ping KV', err); throw new Error(`Failed to ping: ${err}`); } } @@ -97,10 +83,7 @@ class KVManager { return { cursor: newCursor, elements }; } catch (err) { - if (!isDevelopment) { - logger.error('Failed to scan keys', err); - } - console.error(err); + logger.error('Failed to scan keys', err); throw new Error(`Failed to scan keys: ${err}`); } } @@ -111,10 +94,7 @@ class KVManager { const value = await client.get(key); return value ? (JSON.parse(value) as T) : null; } catch (err) { - if (!isDevelopment) { - logger.error('Failed to get key', err); - } - console.error(err); + logger.error('Failed to get key', err); throw new Error(`Failed to get key: ${err}`); } } @@ -144,10 +124,7 @@ class KVManager { return await client.set(key, stringifiedValue, 'EX', options.ex); } } catch (err) { - if (!isDevelopment) { - logger.error('Failed to set key', err); - } - console.error(err); + logger.error('Failed to set key', err); throw new Error(`Failed to set key: ${err}`); } } @@ -158,10 +135,7 @@ class KVManager { const result = await client.incr(key); return result; } catch (err) { - if (!isDevelopment) { - logger.error('Failed to increment key', err); - } - console.error(err); + logger.error('Failed to increment key', err); throw new Error(`Failed to increment key: ${err}`); } } diff --git a/apps/web/src/utils/datastores/postgres/index.ts b/apps/web/src/utils/datastores/postgres/index.ts index 02e69bd133c..4380ecbccb2 100644 --- a/apps/web/src/utils/datastores/postgres/index.ts +++ b/apps/web/src/utils/datastores/postgres/index.ts @@ -26,11 +26,7 @@ function createDefaultPostgresManager() { const dialect = new PostgresDialect({ pool }); return new Kysely({ dialect }); } catch (error) { - if (isDevelopment) { - console.error('Failed to connect to postgres', error); - } else { - logger.error('Failed to connect to postgres', error); - } + logger.error('Failed to connect to postgres', error); throw new Error(`Failed to connect to postgres: ${error}`); } } diff --git a/apps/web/src/utils/frames/basenames.ts b/apps/web/src/utils/frames/basenames.ts index b80eaa8ce5f..cc79d10912f 100644 --- a/apps/web/src/utils/frames/basenames.ts +++ b/apps/web/src/utils/frames/basenames.ts @@ -1,6 +1,7 @@ import { createPublicClient, http } from 'viem'; import type { TransactionReceipt } from 'viem'; import type { Chain } from 'viem/chains'; +import { logger } from 'apps/web/src/utils/logger'; export enum RawErrorStrings { Unavailable = 'Name unavailable', @@ -22,6 +23,6 @@ export async function getTransactionStatus(chain: Chain, transactionId: string) const txStatus = tx.status; return txStatus; } catch (error) { - console.error('Could not get transaction receipt:', error); + logger.error('Could not get transaction receipt', error); } } diff --git a/apps/web/src/utils/logger.ts b/apps/web/src/utils/logger.ts index 786ebe767a2..e80140da626 100644 --- a/apps/web/src/utils/logger.ts +++ b/apps/web/src/utils/logger.ts @@ -1,4 +1,5 @@ // lib/logger.ts +/* eslint-disable no-console */ import type { Tracer } from 'dd-trace'; import { bugsnagNotify } from 'apps/web/src/utils/bugsnag'; diff --git a/libs/base-ui/contexts/Experiments.tsx b/libs/base-ui/contexts/Experiments.tsx index 313a813da8e..d5da63c4b12 100644 --- a/libs/base-ui/contexts/Experiments.tsx +++ b/libs/base-ui/contexts/Experiments.tsx @@ -10,7 +10,7 @@ import React, { } from 'react'; import { Experiment, ExperimentClient } from '@amplitude/experiment-js-client'; -import { ampDeploymentKey } from '../constants'; +import { ampDeploymentKey, isDevelopment } from '../constants'; import logEvent, { ActionType, AnalyticsEventImportance, ComponentType } from '../utils/logEvent'; declare const window: WindowWithAnalytics; @@ -76,7 +76,10 @@ export default function ExperimentsProvider({ children }: ExperimentsProviderPro return undefined; } if (!experimentClient) { - console.error('No experiment clients found'); + if (isDevelopment) { + // eslint-disable-next-line no-console + console.error('No experiment clients found'); + } return undefined; } const variant = experimentClient.variant(flagKey); diff --git a/libs/base-ui/hooks/useSprig.ts b/libs/base-ui/hooks/useSprig.ts index c4e95a0c6d2..acf81d46892 100644 --- a/libs/base-ui/hooks/useSprig.ts +++ b/libs/base-ui/hooks/useSprig.ts @@ -23,7 +23,10 @@ export default function useSprig(environmentId: SprigEnvironmentId) { void sprigInit('track', 'pageload'); setSprig(sprigInit); } catch (error) { - console.error('Failed to load the Sprig module:', error); + if (isDevelopment) { + // eslint-disable-next-line no-console + console.error('Failed to load the Sprig module:', error); + } } };