diff --git a/.gitignore b/.gitignore index a9f2df6..2a4f1f7 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,9 @@ /.next/ /out/ +# vscode +.vscode + # production /build diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..71a1abd --- /dev/null +++ b/.prettierignore @@ -0,0 +1,18 @@ +# Build directories +dist/ +.astro/ +.next/ + +# Node modules +node_modules/ + +# Package lock files +bun.lock +package-lock.json +yarn.lock + +# Other generated files +.DS_Store +.env +.env.* +!.env.example \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..9f5382b --- /dev/null +++ b/.prettierrc @@ -0,0 +1,27 @@ +{ + "arrowParens": "always", + "bracketSpacing": true, + "endOfLine": "lf", + "htmlWhitespaceSensitivity": "css", + "insertPragma": false, + "bracketSameLine": false, + "jsxSingleQuote": false, + "printWidth": 100, + "proseWrap": "preserve", + "quoteProps": "as-needed", + "requirePragma": false, + "semi": true, + "singleQuote": false, + "tabWidth": 2, + "trailingComma": "es5", + "useTabs": false, + "plugins": ["prettier-plugin-astro"], + "overrides": [ + { + "files": "*.astro", + "options": { + "parser": "astro" + } + } + ] +} diff --git a/astro.config.mjs b/astro.config.mjs index 847a6f2..f0dfc2a 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,8 +1,8 @@ -import { defineConfig } from 'astro/config'; +import { defineConfig } from "astro/config"; -import react from '@astrojs/react'; -import tailwindcss from '@tailwindcss/vite'; -import icon from 'astro-icon'; +import react from "@astrojs/react"; +import tailwindcss from "@tailwindcss/vite"; +import icon from "astro-icon"; // https://astro.build/config export default defineConfig({ @@ -10,34 +10,34 @@ export default defineConfig({ react(), icon({ include: { - ph: ['*'], - logos: ['*'], - mdi: ['*'] - } - }) + ph: ["*"], + logos: ["*"], + mdi: ["*"], + }, + }), ], vite: { plugins: [tailwindcss()], server: { - host: '0.0.0.0', + host: "0.0.0.0", allowedHosts: true, cors: { - origin: '*', - methods: ['GET'], - allowedHeaders: ['X-Requested-With'] - } + origin: "*", + methods: ["GET"], + allowedHeaders: ["X-Requested-With"], + }, }, preview: { - host: '0.0.0.0', - allowedHosts: ['jleibl.net'], + host: "0.0.0.0", + allowedHosts: ["jleibl.net"], port: 4321, cors: { - origin: '*', - methods: ['GET'], - allowedHeaders: ['X-Requested-With'] - } - } - } -}); \ No newline at end of file + origin: "*", + methods: ["GET"], + allowedHeaders: ["X-Requested-With"], + }, + }, + }, +}); diff --git a/bun.lock b/bun.lock index e3fae79..9a77b57 100644 --- a/bun.lock +++ b/bun.lock @@ -30,7 +30,10 @@ "@types/react-dom": "^19.0.4", "eslint": "^9", "eslint-config-next": "15.1.7", + "eslint-config-prettier": "^10.1.1", "postcss": "^8", + "prettier": "^3.5.3", + "prettier-plugin-astro": "^0.14.1", "tailwindcss": "^4.0.12", "typescript": "^5.7.3", }, @@ -705,6 +708,8 @@ "eslint-config-next": ["eslint-config-next@15.1.7", "", { "dependencies": { "@next/eslint-plugin-next": "15.1.7", "@rushstack/eslint-patch": "^1.10.3", "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-react": "^7.37.0", "eslint-plugin-react-hooks": "^5.0.0" }, "peerDependencies": { "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", "typescript": ">=3.3.1" }, "optionalPeers": ["typescript"] }, "sha512-zXoMnYUIy3XHaAoOhrcYkT9UQWvXqWju2K7NNsmb5wd/7XESDwof61eUdW4QhERr3eJ9Ko/vnXqIrj8kk/drYw=="], + "eslint-config-prettier": ["eslint-config-prettier@10.1.1", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-4EQQr6wXwS+ZJSzaR5ZCrYgLxqvUjdXctaEtBqHcbkW944B1NQyO4qpdHQbXBONfwxXdkAY81HH4+LUfrg+zPw=="], + "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.9", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", "resolve": "^1.22.4" } }, "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g=="], "eslint-import-resolver-typescript": ["eslint-import-resolver-typescript@3.8.3", "", { "dependencies": { "@nolyfill/is-core-module": "1.0.39", "debug": "^4.3.7", "enhanced-resolve": "^5.15.0", "get-tsconfig": "^4.10.0", "is-bun-module": "^1.0.2", "stable-hash": "^0.0.4", "tinyglobby": "^0.2.12" }, "peerDependencies": { "eslint": "*", "eslint-plugin-import": "*", "eslint-plugin-import-x": "*" }, "optionalPeers": ["eslint-plugin-import", "eslint-plugin-import-x"] }, "sha512-A0bu4Ks2QqDWNpeEgTQMPTngaMhuDu4yv6xpftBMAf+1ziXnpx+eSR1WRfoPTe2BAiAjHFZ7kSNx1fvr5g5pmQ=="], @@ -1243,7 +1248,9 @@ "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], - "prettier": ["prettier@2.8.7", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw=="], + "prettier": ["prettier@3.5.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw=="], + + "prettier-plugin-astro": ["prettier-plugin-astro@0.14.1", "", { "dependencies": { "@astrojs/compiler": "^2.9.1", "prettier": "^3.0.0", "sass-formatter": "^0.7.6" } }, "sha512-RiBETaaP9veVstE4vUwSIcdATj6dKmXljouXc/DDNwBSPTp8FRkLGDSGFClKsAFeeg+13SB0Z1JZvbD76bigJw=="], "prismjs": ["prismjs@1.29.0", "", {}, "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q=="], @@ -1333,6 +1340,8 @@ "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + "s.color": ["s.color@0.0.15", "", {}, "sha512-AUNrbEUHeKY8XsYr/DYpl+qk5+aM+DChopnWOPEzn8YKzOhv4l2zH6LzZms3tOZP3wwdOyc0RmTciyi46HLIuA=="], + "safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="], "safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="], @@ -1341,6 +1350,8 @@ "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + "sass-formatter": ["sass-formatter@0.7.9", "", { "dependencies": { "suf-log": "^2.5.3" } }, "sha512-CWZ8XiSim+fJVG0cFLStwDvft1VI7uvXdCNJYXhDvowiv+DsbD1nXLiQ4zrE5UBvj5DWZJ93cwN0NX5PMsr1Pw=="], + "scheduler": ["scheduler@0.25.0", "", {}, "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA=="], "semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], @@ -1407,6 +1418,8 @@ "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="], + "suf-log": ["suf-log@2.5.3", "", { "dependencies": { "s.color": "0.0.15" } }, "sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow=="], + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], @@ -1683,6 +1696,8 @@ "yaml-language-server/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + "yaml-language-server/prettier": ["prettier@2.8.7", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw=="], + "yaml-language-server/request-light": ["request-light@0.5.8", "", {}, "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg=="], "yaml-language-server/vscode-languageserver": ["vscode-languageserver@7.0.0", "", { "dependencies": { "vscode-languageserver-protocol": "3.16.0" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw=="], diff --git a/eslint.config.mjs b/eslint.config.mjs index c85fb67..313612b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -11,6 +11,7 @@ const compat = new FlatCompat({ const eslintConfig = [ ...compat.extends("next/core-web-vitals", "next/typescript"), + ...compat.extends("prettier"), ]; export default eslintConfig; diff --git a/package.json b/package.json index 3f72548..1899ee1 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,9 @@ "build": "astro build", "preview": "astro preview", "start": "astro dev --host", - "lint": "astro check" + "lint": "astro check", + "format": "prettier --write .", + "format:check": "prettier --check ." }, "dependencies": { "@astrojs/check": "^0.9.4", @@ -36,7 +38,10 @@ "@types/react-dom": "^19.0.4", "eslint": "^9", "eslint-config-next": "15.1.7", + "eslint-config-prettier": "^10.1.1", "postcss": "^8", + "prettier": "^3.5.3", + "prettier-plugin-astro": "^0.14.1", "tailwindcss": "^4.0.12", "typescript": "^5.7.3" } diff --git a/public/manifest.json b/public/manifest.json index 832477b..a76a5c1 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -46,4 +46,4 @@ "icons": [{ "src": "/icons/contact-icon.png", "sizes": "192x192" }] } ] -} \ No newline at end of file +} diff --git a/public/service-worker.js b/public/service-worker.js index 5983414..95ca615 100644 --- a/public/service-worker.js +++ b/public/service-worker.js @@ -1,163 +1,146 @@ -const CACHE_NAME = 'jleibl-portfolio-v1'; +const CACHE_NAME = "jleibl-portfolio-v1"; -const PRECACHE_ASSETS = [ - '/', - '/index.html' -]; +const PRECACHE_ASSETS = ["/", "/index.html"]; -const AUTH_ASSETS = [ - '/manifest.json' -]; +const AUTH_ASSETS = ["/manifest.json"]; -const STYLE_ASSETS = [ - '/styles/global.css', - '/styles/theme.css' -]; +const STYLE_ASSETS = ["/styles/global.css", "/styles/theme.css"]; -const IMAGE_ASSETS = [ - '/images/profile-image.jpg' -]; +const IMAGE_ASSETS = ["/images/profile-image.jpg"]; -const ALL_ASSETS = [ - ...PRECACHE_ASSETS, - ...AUTH_ASSETS, - ...STYLE_ASSETS, - ...IMAGE_ASSETS -]; +const ALL_ASSETS = [...PRECACHE_ASSETS, ...AUTH_ASSETS, ...STYLE_ASSETS, ...IMAGE_ASSETS]; -self.addEventListener('install', event => { +self.addEventListener("install", (event) => { self.skipWaiting(); event.waitUntil( - caches.open(CACHE_NAME) - .then(cache => { - console.log('Opened cache'); - return cache.addAll(PRECACHE_ASSETS); - }) + caches.open(CACHE_NAME).then((cache) => { + console.log("Opened cache"); + return cache.addAll(PRECACHE_ASSETS); + }) ); }); -self.addEventListener('activate', event => { +self.addEventListener("activate", (event) => { event.waitUntil( - caches.keys().then(cacheNames => { + caches.keys().then((cacheNames) => { return Promise.all( - cacheNames.map(cacheName => { + cacheNames.map((cacheName) => { if (cacheName !== CACHE_NAME) { - console.log('Deleting old cache:', cacheName); + console.log("Deleting old cache:", cacheName); return caches.delete(cacheName); } }) ); }) ); - + return self.clients.claim(); }); function shouldCache(url) { const urlObj = new URL(url); - - if (urlObj.hostname.includes('cloudflareaccess.com')) { + + if (urlObj.hostname.includes("cloudflareaccess.com")) { return false; } - + if (urlObj.origin === self.location.origin) { - if (urlObj.pathname.endsWith('.html') || - urlObj.pathname.endsWith('.css') || - urlObj.pathname.endsWith('.js') || - urlObj.pathname.endsWith('.jpg') || - urlObj.pathname.endsWith('.jpeg') || - urlObj.pathname.endsWith('.png') || - urlObj.pathname.endsWith('.svg') || - urlObj.pathname.endsWith('.webp') || - urlObj.pathname.endsWith('.woff') || - urlObj.pathname.endsWith('.woff2') || - urlObj.pathname.endsWith('.json')) { + if ( + urlObj.pathname.endsWith(".html") || + urlObj.pathname.endsWith(".css") || + urlObj.pathname.endsWith(".js") || + urlObj.pathname.endsWith(".jpg") || + urlObj.pathname.endsWith(".jpeg") || + urlObj.pathname.endsWith(".png") || + urlObj.pathname.endsWith(".svg") || + urlObj.pathname.endsWith(".webp") || + urlObj.pathname.endsWith(".woff") || + urlObj.pathname.endsWith(".woff2") || + urlObj.pathname.endsWith(".json") + ) { return true; } - - if (urlObj.pathname === '/') { + + if (urlObj.pathname === "/") { return true; } } - + return false; } function isAuthProtectedAsset(url) { const urlPath = new URL(url).pathname; - return AUTH_ASSETS.some(asset => asset === urlPath); + return AUTH_ASSETS.some((asset) => asset === urlPath); } -self.addEventListener('fetch', event => { +self.addEventListener("fetch", (event) => { if (isAuthProtectedAsset(event.request.url)) { event.respondWith( fetch(event.request) - .then(response => { + .then((response) => { if (response.status === 200) { const responseToCache = response.clone(); - caches.open(CACHE_NAME) - .then(cache => { - cache.put(event.request, responseToCache); - }); + caches.open(CACHE_NAME).then((cache) => { + cache.put(event.request, responseToCache); + }); } return response; }) - .catch(error => { - console.log('Fetch failed for auth protected asset:', error); + .catch((error) => { + console.log("Fetch failed for auth protected asset:", error); return caches.match(event.request); }) ); return; } - if (event.request.mode === 'navigate' || shouldCache(event.request.url)) { + if (event.request.mode === "navigate" || shouldCache(event.request.url)) { event.respondWith( - caches.match(event.request) - .then(response => { - if (response) { - return response; - } - - const fetchRequest = event.request.clone(); - - return fetch(fetchRequest) - .then(response => { - if (response.url.includes('cloudflareaccess.com')) { - console.log('Request redirected to authentication page:', response.url); - return response; - } - - if (!response || response.status !== 200 || response.type !== 'basic') { - return response; - } - - const responseToCache = response.clone(); - - if (shouldCache(new URL(event.request.url))) { - caches.open(CACHE_NAME) - .then(cache => { - cache.put(event.request, responseToCache); - }); - } - + caches.match(event.request).then((response) => { + if (response) { + return response; + } + + const fetchRequest = event.request.clone(); + + return fetch(fetchRequest) + .then((response) => { + if (response.url.includes("cloudflareaccess.com")) { + console.log("Request redirected to authentication page:", response.url); return response; - }) - .catch(error => { - console.log('Fetch failed:', error); - - if (event.request.mode === 'navigate') { - return caches.match('/'); - } - - return null; - }); - }) + } + + if (!response || response.status !== 200 || response.type !== "basic") { + return response; + } + + const responseToCache = response.clone(); + + if (shouldCache(new URL(event.request.url))) { + caches.open(CACHE_NAME).then((cache) => { + cache.put(event.request, responseToCache); + }); + } + + return response; + }) + .catch((error) => { + console.log("Fetch failed:", error); + + if (event.request.mode === "navigate") { + return caches.match("/"); + } + + return null; + }); + }) ); } }); -self.addEventListener('message', event => { - if (event.data && event.data.type === 'SKIP_WAITING') { +self.addEventListener("message", (event) => { + if (event.data && event.data.type === "SKIP_WAITING") { self.skipWaiting(); } -}); \ No newline at end of file +}); diff --git a/src/components/layout/Footer.astro b/src/components/layout/Footer.astro index 91957b1..bbbf473 100644 --- a/src/components/layout/Footer.astro +++ b/src/components/layout/Footer.astro @@ -3,13 +3,13 @@ import FadeIn from "../ui/FadeIn.astro"; import { FaGithub, FaLinkedin, FaEnvelope } from "react-icons/fa"; --- -