diff --git a/.eslintrc.js b/.eslintrc.js index 9d129fd7..4a2f06cc 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,11 +1,16 @@ module.exports = { - plugins: ['prettier'], - extends: ['next/core-web-vitals'], + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + 'plugin:@next/next/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.js'], + parser: '@typescript-eslint/parser', + plugins: ['react-refresh'], rules: { - 'no-console': 'error', - 'prettier/prettier': 'warn', - 'react-hooks/exhaustive-deps': 'off', - 'react/display-name': 'off', - 'import/no-anonymous-default-export': 'off', + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], }, }; diff --git a/package.json b/package.json index 2fc795c5..4d210fa1 100644 --- a/package.json +++ b/package.json @@ -19,40 +19,37 @@ "start": "next start", "build": "next build", "dev": "next dev", - "next": "next", - "preexport": "next build", "export": "next export", - "lint": "eslint .", + "lint": "next lint", "prepare": "husky install" }, "dependencies": { - "@m4tt72/matomo-tracker-react": "^0.6.2", - "axios": "^1.6.0", + "axios": "^1.6.2", "cowsay-browser": "^1.1.8", - "date-fns": "^2.30.0", - "next": "^14.0.1", + "date-fns": "^3.0.6", + "next": "^14.0.4", "react": "^18.1.0", "react-dom": "^18.1.0", - "react-icons": "^4.11.0" + "react-icons": "^4.12.0" }, "devDependencies": { - "@types/node": "^20.8.10", - "@types/react": "^18.2.33", - "@typescript-eslint/eslint-plugin": "^6.9.1", - "@typescript-eslint/parser": "^6.9.1", + "@next/eslint-plugin-next": "^14.0.4", + "@types/node": "^20.10.5", + "@types/react": "^18.2.45", + "@typescript-eslint/eslint-plugin": "^6.15.0", + "@typescript-eslint/parser": "^6.15.0", "autoprefixer": "^10.4.16", - "eslint": "8.52.0", - "eslint-config-next": "^14.0.1", - "eslint-plugin-next": "^0.0.0", - "eslint-plugin-prettier": "^5.0.1", - "eslint-plugin-react": "^7.33.2", + "eslint": "^8", + "eslint-config-next": "^14.0.4", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.5", "husky": "^8.0.3", - "postcss": "^8.4.31", - "prettier": "^3.0.3", - "tailwindcss": "^3.3.5", - "typescript": "^5.2.2" + "postcss": "^8.4.32", + "prettier": "^3.1.1", + "tailwindcss": "^3.4.0", + "typescript": "^5" }, "engines": { - "node": ">=18.17.0" + "node": ">=20.10.0" } } diff --git a/src/components/input/Input.tsx b/src/components/input/Input.tsx index ca76a2ab..23c71cbb 100644 --- a/src/components/input/Input.tsx +++ b/src/components/input/Input.tsx @@ -1,4 +1,3 @@ -import { useMatomo } from '@m4tt72/matomo-tracker-react'; import React, { useEffect, useState } from 'react'; import { commandExists } from '../../utils/commandExists'; import { useShell } from '../../utils/shellProvider'; @@ -7,7 +6,6 @@ import { useTheme } from '../../utils/themeProvider'; import { Ps1 } from '../ps1'; export const Input = ({ inputRef, containerRef }) => { - const { trackEvent } = useMatomo(); const { theme } = useTheme(); const [value, setValue] = useState(''); const { @@ -21,7 +19,7 @@ export const Input = ({ inputRef, containerRef }) => { useEffect(() => { containerRef.current.scrollTo(0, containerRef.current.scrollHeight); - }, [history]); + }, [history, containerRef]); const onSubmit = async (event: React.KeyboardEvent) => { const commands: string[] = history @@ -58,11 +56,6 @@ export const Input = ({ inputRef, containerRef }) => { setCommand(value); setValue(''); - - trackEvent({ - category: 'Command Executed', - action: value || 'no command', - }); } if (event.key === 'ArrowUp') { diff --git a/src/components/ps1/Ps1.tsx b/src/components/ps1/Ps1.tsx index 9efd19aa..e7f3dbca 100644 --- a/src/components/ps1/Ps1.tsx +++ b/src/components/ps1/Ps1.tsx @@ -6,9 +6,7 @@ export const Ps1 = () => { const { theme } = useTheme(); useEffect(() => { - if (typeof window !== undefined) { - setHostname(window.location.hostname); - } + setHostname(window.location.hostname); }, []); return ( diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 570ec791..2088183a 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,4 +1,3 @@ -import { createInstance, MatomoProvider } from '@m4tt72/matomo-tracker-react'; import Head from 'next/head'; import React, { useEffect } from 'react'; import { Layout } from '../components/layout'; @@ -49,25 +48,5 @@ const App = ({ Component, pageProps }) => { }; export default (props) => { - const ENABLE_TRACKING = Boolean(+process.env.NEXT_PUBLIC_ENABLE_TRACKING); - - if (!ENABLE_TRACKING) { - return ; - } - - const instance = createInstance({ - urlBase: process.env.NEXT_PUBLIC_TRACKING_URL, - trackerUrl: `${process.env.NEXT_PUBLIC_TRACKING_URL}/js/`, - srcUrl: `${process.env.NEXT_PUBLIC_TRACKING_URL}/js/`, - siteId: +process.env.NEXT_PUBLIC_TRACKING_SITE_ID, - configurations: { - setRequestMethod: 'GET', - }, - }); - - return ( - - - - ); + return ; }; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index b91fade4..1b079724 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,4 +1,3 @@ -import { useMatomo } from '@m4tt72/matomo-tracker-react'; import Head from 'next/head'; import React from 'react'; import { History } from '../components/history'; @@ -12,21 +11,16 @@ interface IndexPageProps { } const IndexPage: React.FC = ({ inputRef }) => { - const { trackPageView } = useMatomo(); const { history } = useShell(); const { theme } = useTheme(); const containerRef = React.useRef(null); - React.useEffect(() => { - trackPageView({}); - }, []); - React.useEffect(() => { if (inputRef.current) { inputRef.current.focus(); } - }, [history]); + }, [history, inputRef]); return ( <> diff --git a/src/utils/bin/about.ts b/src/utils/bin/about.ts index c9b01445..cf2fdb1e 100644 --- a/src/utils/bin/about.ts +++ b/src/utils/bin/about.ts @@ -1,6 +1,6 @@ import { getBio } from '../../api'; -export const about = async (args: string[]): Promise => { +export const about = async (_args: string[]): Promise => { const bio = await getBio(); return bio; diff --git a/src/utils/bin/neofetch.ts b/src/utils/bin/neofetch.ts index af449df2..31b7d038 100644 --- a/src/utils/bin/neofetch.ts +++ b/src/utils/bin/neofetch.ts @@ -9,17 +9,17 @@ const macos = ` OMMM0, .;loddo:' loolloddol;. cKMMMMMMMMMMNWMMMMMMMMMM0: - .KMMMMMMMMMMMMMMMMMMMMMMMWd. - XMMMMMMMMMMMMMMMMMMMMMMMX. -;MMMMMMMMMMMMMMMMMMMMMMMM: -:MMMMMMMMMMMMMMMMMMMMMMMM: -.MMMMMMMMMMMMMMMMMMMMMMMMX. - kMMMMMMMMMMMMMMMMMMMMMMMMWd. - .XMMMMMMMMMMMMMMMMMMMMMMMMMMk - .XMMMMMMMMMMMMMMMMMMMMMMMMK. - kMMMMMMMMMMMMMMMMMMMMMMd - ;KMMMMMMMWXXWMMMMMMMk. - .cooc,. .,coo:. + .KMMMMMMMMMMMMMMMMMMMMMMMWd. + XMMMMMMMMMMMMMMMMMMMMMMMX. +;MMMMMMMMMMMMMMMMMMMMMMMM: +:MMMMMMMMMMMMMMMMMMMMMMMM: +.MMMMMMMMMMMMMMMMMMMMMMMMX. + kMMMMMMMMMMMMMMMMMMMMMMMMWd. + .XMMMMMMMMMMMMMMMMMMMMMMMMMMk + .XMMMMMMMMMMMMMMMMMMMMMMMMK. + kMMMMMMMMMMMMMMMMMMMMMMd + ;KMMMMMMMWXXWMMMMMMMk. + .cooc,. .,coo:. `; const windows = ` @@ -32,34 +32,34 @@ llllllllllllll lllllllllllllllllll llllllllllllll lllllllllllllllllll llllllllllllll lllllllllllllllllll llllllllllllll lllllllllllllllllll - + llllllllllllll lllllllllllllllllll llllllllllllll lllllllllllllllllll llllllllllllll lllllllllllllllllll llllllllllllll lllllllllllllllllll llllllllllllll lllllllllllllllllll \`'ccllllllllll lllllllllllllllllll - \`' \*:: :ccllllllllllllllll + \`' *:: :ccllllllllllllllll \`\`\`\`''*::cll `; const linux = ` - .-/+oossssoo+/-. - \`:+ssssssssssssssssss+:\` - -+ssssssssssssssssssyyssss+- - .ossssssssssssssssssdMMMNysssso. - /ssssssssssshdmmNNmmyNMMMMhssssss/ - +ssssssssshmydMMMMMMMNddddyssssssss+ - /sssssssshNMMMyhhyyyyhmNMMMNhssssssss/ -.ssssssssdMMMNhsssssssssshNMMMdssssssss. -+sssshhhyNMMNyssssssssssssyNMMMysssssss+ -ossyNMMMNyMMhsssssssssssssshmmmhssssssso -ossyNMMMNyMMhsssssssssssssshmmmhssssssso -+sssshhhyNMMNyssssssssssssyNMMMysssssss+ -.ssssssssdMMMNhsssssssssshNMMMdssssssss. - /sssssssshNMMMyhhyyyyhdNMMMNhssssssss/ - +sssssssssdmydMMMMMMMMddddyssssssss+ - /ssssssssssshdmNNNNmyNMMMMhssssss/ + .-/+oossssoo+/-. + \`:+ssssssssssssssssss+:\` + -+ssssssssssssssssssyyssss+- + .ossssssssssssssssssdMMMNysssso. + /ssssssssssshdmmNNmmyNMMMMhssssss/ + +ssssssssshmydMMMMMMMNddddyssssssss+ + /sssssssshNMMMyhhyyyyhmNMMMNhssssssss/ +.ssssssssdMMMNhsssssssssshNMMMdssssssss. ++sssshhhyNMMNyssssssssssssyNMMMysssssss+ +ossyNMMMNyMMhsssssssssssssshmmmhssssssso +ossyNMMMNyMMhsssssssssssssshmmmhssssssso ++sssshhhyNMMNyssssssssssssyNMMMysssssss+ +.ssssssssdMMMNhsssssssssshNMMMdssssssss. + /sssssssshNMMMyhhyyyyhdNMMMNhssssssss/ + +sssssssssdmydMMMMMMMMddddyssssssss+ + /ssssssssssshdmNNNNmyNMMMMhssssss/ .ossssssssssssssssssdMMMNysssso. -+sssssssssssssssssyyyssss+- \`:+ssssssssssssssssss+:\` @@ -147,7 +147,7 @@ const getInfo = () => { return message; }; -export const neofetch = async (args?: string[]): Promise => { +export const neofetch = async (_args?: string[]): Promise => { const art = getArt(); const info = getInfo(); diff --git a/src/utils/bin/projects.ts b/src/utils/bin/projects.ts index 61a1d545..38724c98 100644 --- a/src/utils/bin/projects.ts +++ b/src/utils/bin/projects.ts @@ -1,6 +1,6 @@ import { getProjects } from '../../api'; -export const projects = async (args: string[]): Promise => { +export const projects = async (_args: string[]): Promise => { const projects = await getProjects(); return projects diff --git a/src/utils/bin/social.ts b/src/utils/bin/social.ts index 78e4a870..91c9b07d 100644 --- a/src/utils/bin/social.ts +++ b/src/utils/bin/social.ts @@ -1,18 +1,18 @@ import config from '../../../config.json'; -export const instagram = async (args: string[]): Promise => { +export const instagram = async (_args: string[]): Promise => { window.open(`https://www.instagram.com/${config.social.instagram}/`); return 'Opening instagram...'; }; -export const github = async (args: string[]): Promise => { +export const github = async (_args: string[]): Promise => { window.open(`https://github.com/${config.social.github}/`); return 'Opening github...'; }; -export const linkedin = async (args: string[]): Promise => { +export const linkedin = async (_args: string[]): Promise => { window.open(`https://www.linkedin.com/in/${config.social.linkedin}/`); return 'Opening linkedin...'; diff --git a/src/utils/bin/theme.ts b/src/utils/bin/theme.ts index 5dcfbe5d..19755a17 100644 --- a/src/utils/bin/theme.ts +++ b/src/utils/bin/theme.ts @@ -1,35 +1,45 @@ import Themes from '../../../themes.json'; -export const theme = async ( - args: string[], - callback?: (value: string) => string, -): Promise => { - if (args.length === 0) { - return `Usage: theme [arg] +const help = `Usage: theme [arg] Args: - ls: list all themes - set: set a theme - random: set a random theme -Example: +Example: theme ls # to list all themes theme set Gruvbox # to set a theme`; + +export const theme = async ( + args: string[], + callback?: (value: string) => string, +): Promise => { + if (args.length === 0) { + return help; } switch (args[0]) { - case 'ls': + case 'ls': { let result = Themes.map((theme) => theme.name.toLowerCase()).join(', '); result += '\n\n'; result += `You can preview all these themes in the docs`; return result; - case 'set': + } + + case 'set': { const selectedTheme = args[1]; return callback(selectedTheme); - case 'random': + } + + case 'random': { const randomTheme = Themes[Math.floor(Math.random() * Themes.length)]; return callback(randomTheme.name.toLowerCase()); + } + + default: + return help; } }; diff --git a/src/utils/bin/utils.ts b/src/utils/bin/utils.ts index a64498ed..5d99719a 100644 --- a/src/utils/bin/utils.ts +++ b/src/utils/bin/utils.ts @@ -1,7 +1,7 @@ import packageJson from '../../../package.json'; import * as bin from './index'; -export const help = async (args: string[]): Promise => { +export const help = async (_args: string[]): Promise => { const commands = Object.keys(bin).sort().join(', '); return `Available commands:\n${commands}\n\n[tab]\t trigger completion.\n[ctrl+l] clear terminal.\n[ctrl+c] cancel command.`; @@ -11,35 +11,35 @@ export const echo = async (args: string[]): Promise => { return args.join(' '); }; -export const whoami = async (args: string[]): Promise => { +export const whoami = async (_args: string[]): Promise => { return 'guest'; }; -export const date = async (args: string[]): Promise => { +export const date = async (_args: string[]): Promise => { return new Date().toString(); }; -export const gui = async (args: string[]): Promise => { +export const gui = async (_args: string[]): Promise => { window.open('https://m4tt72.com', '_self'); return 'Opening GUI version...'; }; -export const email = async (args: string[]): Promise => { +export const email = async (_args: string[]): Promise => { window.open('mailto:hi@m4tt72.com'); return 'Opening mailto:hi@m4tt72.com...'; }; -export const vi = async (args: string[]): Promise => { +export const vi = async (_args: string[]): Promise => { return `why use vi? try 'emacs'.`; }; -export const vim = async (args: string[]): Promise => { +export const vim = async (_args: string[]): Promise => { return `why use vim? try 'emacs'.`; }; -export const emacs = async (args?: string[]): Promise => { +export const emacs = async (_args?: string[]): Promise => { return `really? emacs? you should be using 'vim'`; }; @@ -51,7 +51,7 @@ export const sudo = async (args?: string[]): Promise => { return `Permission denied: unable to run the command '${args[0]}' as root.`; }; -export const repo = async (args?: string[]): Promise => { +export const repo = async (_args?: string[]): Promise => { setTimeout(function () { window.open('https://github.com/m4tt72/terminal', '_blank'); }, 1000); @@ -59,13 +59,13 @@ export const repo = async (args?: string[]): Promise => { return 'Opening repository...'; }; -export const donate = async (args?: string[]): Promise => { +export const donate = async (_args?: string[]): Promise => { window.open(packageJson.funding.url, '_blank'); return 'Opening donation url...'; }; -export const banner = (args?: string[]): string => { +export const banner = (_args?: string[]): string => { return ` ███╗ ███╗██╗ ██╗████████╗████████╗███████╗██████╗ ████╗ ████║██║ ██║╚══██╔══╝╚══██╔══╝╚════██║╚════██╗ diff --git a/src/utils/shellProvider.tsx b/src/utils/shellProvider.tsx index c681ba63..1f3ebedc 100644 --- a/src/utils/shellProvider.tsx +++ b/src/utils/shellProvider.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useCallback, useEffect } from 'react'; import { History } from '../interfaces/history'; import * as bin from './bin'; import { useTheme } from './themeProvider'; @@ -38,45 +38,38 @@ export const ShellProvider: React.FC = ({ children }) => { const [history, _setHistory] = React.useState([]); const [command, _setCommand] = React.useState(''); const [lastCommandIndex, _setLastCommandIndex] = React.useState(0); - const { theme, setTheme } = useTheme(); - - useEffect(() => { - setCommand('banner'); - }, []); + const { setTheme } = useTheme(); + + const setHistory = useCallback( + (output: string) => { + _setHistory((h) => [ + ...h, + { + id: h.length, + date: new Date(), + command: command.split(' ').slice(1).join(' '), + output, + }, + ]); + }, + [command], + ); - useEffect(() => { - if (!init) { - execute(); - } - }, [command, init]); - - const setHistory = (output: string) => { - _setHistory([ - ...history, - { - id: history.length, - date: new Date(), - command: command.split(' ').slice(1).join(' '), - output, - }, - ]); - }; - - const setCommand = (command: string) => { + const setCommand = useCallback((command: string) => { _setCommand([Date.now(), command].join(' ')); setInit(false); - }; + }, []); - const clearHistory = () => { + const clearHistory = useCallback(() => { _setHistory([]); - }; + }, []); - const setLastCommandIndex = (index: number) => { + const setLastCommandIndex = useCallback((index: number) => { _setLastCommandIndex(index); - }; + }, []); - const execute = async () => { + const execute = useCallback(async () => { const [cmd, ...args] = command.split(' ').slice(1); if (isTrackingEnabled) { @@ -86,12 +79,14 @@ export const ShellProvider: React.FC = ({ children }) => { } switch (cmd) { - case 'theme': + case 'theme': { const output = await bin.theme(args, setTheme); setHistory(output); break; + } + case 'clear': clearHistory(); break; @@ -112,7 +107,17 @@ export const ShellProvider: React.FC = ({ children }) => { } } } - }; + }, [command, setTheme, setHistory, clearHistory]); + + useEffect(() => { + setCommand('banner'); + }, [setCommand]); + + useEffect(() => { + if (!init) { + execute(); + } + }, [command, init, execute]); return (