diff --git a/.eslintrc.json b/.eslintrc.json index 05be565..8a07304 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -21,6 +21,7 @@ } }, "rules": { + "no-console": "error", "@typescript-eslint/no-unused-vars": "error", "@typescript-eslint/no-explicit-any": "error", "react/react-in-jsx-scope": "off", @@ -31,6 +32,17 @@ "./src/**/*.{js,ts}": "KEBAB_CASE" }, { "ignoreMiddleExtensions": true } + ], + "@typescript-eslint/no-restricted-imports": [ + "error", + { + "paths": [ + { + "name": "next-axiom", + "message": "For client side logging import from log-client, for server actions and server components import from log-server" + } + ] + } ] } } diff --git a/.prettierrc.js b/.prettierrc.js index 6c29d5d..22dcae6 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -31,5 +31,5 @@ module.exports = { ], importOrderParserPlugins: ['typescript', 'jsx', 'decorators-legacy'], importOrderTypeScriptVersion: '5.0.0', - plugins: [require('prettier-plugin-tailwindcss')], + plugins: ['prettier-plugin-tailwindcss'], }; diff --git a/next.config.mjs b/next.config.mjs index 8760414..b3f1e68 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,5 +1,7 @@ await import('./src/env.mjs'); import { withSentryConfig } from '@sentry/nextjs'; +// eslint-disable-next-line @typescript-eslint/no-restricted-imports +import { withAxiom } from 'next-axiom'; /** @type {import('next').NextConfig} */ const nextConfig = { @@ -22,8 +24,10 @@ const nextConfig = { }, }; -export default withSentryConfig(nextConfig, { - silent: true, - org: 'zerodays', - project: 'next-template', -}); +export default withAxiom( + withSentryConfig(nextConfig, { + silent: true, + org: 'zerodays', + project: 'next-template', + }), +); diff --git a/package.json b/package.json index 77688a4..73b9b4b 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@t3-oss/env-nextjs": "^0.7.1", "autoprefixer": "10.4.14", "next": "^14.0.0", + "next-axiom": "^1.1.0", "next-international": "^1.1.3", "postcss": "8.4.31", "react": "^18.2.0", @@ -52,8 +53,8 @@ "husky": "^8.0.3", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", - "prettier": "^3.0.3", - "prettier-plugin-tailwindcss": "^0.5.6", + "prettier": "^3.1.0", + "prettier-plugin-tailwindcss": "^0.5.7", "ts-jest": "^29.1.1", "tsx": "^3.14.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1b79cf5..7f040bc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,9 @@ dependencies: next: specifier: ^14.0.0 version: 14.0.2(@babel/core@7.23.3)(react-dom@18.2.0)(react@18.2.0) + next-axiom: + specifier: ^1.1.0 + version: 1.1.0(next@14.0.2)(react@18.2.0) next-international: specifier: ^1.1.3 version: 1.1.4 @@ -113,10 +116,10 @@ devDependencies: specifier: ^29.7.0 version: 29.7.0 prettier: - specifier: ^3.0.3 + specifier: ^3.1.0 version: 3.1.0 prettier-plugin-tailwindcss: - specifier: ^0.5.6 + specifier: ^0.5.7 version: 0.5.7(@ianvs/prettier-plugin-sort-imports@4.1.1)(prettier@3.1.0) ts-jest: specifier: ^29.1.1 @@ -5084,6 +5087,18 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true + /next-axiom@1.1.0(next@14.0.2)(react@18.2.0): + resolution: {integrity: sha512-/Fh8MOM7gzeR58SA0HfF2ZyNp4WY3cv0hbH3XW3e0u3FG4IGPfFoRuNtpWx3qv4YCqm3jmoR3CVP/dc0VF6xMQ==} + engines: {node: '>=18'} + peerDependencies: + next: '>=13.4' + react: '>=18.0.0' + dependencies: + next: 14.0.2(@babel/core@7.23.3)(react-dom@18.2.0)(react@18.2.0) + react: 18.2.0 + whatwg-fetch: 3.6.19 + dev: false + /next-international@1.1.4: resolution: {integrity: sha512-peIJXXEC5lM7zZONCgN1uUxCkIHpSW1pZuHoRTp9ND14K7CDdHajDMz9RTxVCmQUGWXSaqruM6XVAuq4d+Gpxg==} dependencies: @@ -6642,6 +6657,10 @@ packages: iconv-lite: 0.6.3 dev: true + /whatwg-fetch@3.6.19: + resolution: {integrity: sha512-d67JP4dHSbm2TrpFj8AbO8DnL1JXL5J9u0Kq2xW6d0TFDbCA3Muhdt8orXC22utleTVj7Prqt82baN6RBvnEgw==} + dev: false + /whatwg-mimetype@3.0.0: resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} engines: {node: '>=12'} diff --git a/src/app/[locale]/page.tsx b/src/app/[locale]/page.tsx index 65b645d..07f5fac 100644 --- a/src/app/[locale]/page.tsx +++ b/src/app/[locale]/page.tsx @@ -1,10 +1,15 @@ import { Inter } from 'next/font/google'; import SampleForm from '@/components/sample-form'; +import serverLogger from '@/lib/axiom/log-server'; const inter = Inter({ subsets: ['latin'] }); export default function Page() { + const logger = serverLogger(); + + logger.info('Hello from server logger!'); + return (
diff --git a/src/components/sample-form.tsx b/src/components/sample-form.tsx index 59f3412..000e950 100644 --- a/src/components/sample-form.tsx +++ b/src/components/sample-form.tsx @@ -5,6 +5,7 @@ import { useForm } from 'react-hook-form'; import { useScopedI18n } from '@/i18n/client'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; +import useClientLogger from '@/lib/axiom/log-client'; // Always use zod to validate your form const SampleFormSchema = z @@ -20,6 +21,7 @@ type SampleFormValues = z.infer; // Example form using react-hook-form and zod const SampleForm = () => { const t = useScopedI18n('form'); + const logger = useClientLogger(); const [submitting, setSubmitting] = useState(false); @@ -34,7 +36,7 @@ const SampleForm = () => { const onSubmit = (values: SampleFormValues) => { // Handle form submission - console.log(values); + logger.info('Form values:', values); setSubmitting(true); }; diff --git a/src/lib/axiom/log-client.tsx b/src/lib/axiom/log-client.tsx new file mode 100644 index 0000000..bf57816 --- /dev/null +++ b/src/lib/axiom/log-client.tsx @@ -0,0 +1,11 @@ +// eslint-disable-next-line @typescript-eslint/no-restricted-imports +import { useLogger } from 'next-axiom'; + +// Used by client components to log events +const useClientLogger = () => { + // TODO: add custom data to logs + const logConfig = { todo: 'TODO' }; + return useLogger().with(logConfig); +}; + +export default useClientLogger; diff --git a/src/lib/axiom/log-server.ts b/src/lib/axiom/log-server.ts new file mode 100644 index 0000000..d2d305f --- /dev/null +++ b/src/lib/axiom/log-server.ts @@ -0,0 +1,11 @@ +// eslint-disable-next-line @typescript-eslint/no-restricted-imports +import { Logger } from 'next-axiom'; + +// Used by server actions + server components to log events +const serverLogger = () => { + // TODO: add custom data to logs + const logConfig = { todo: 'TODO' }; + return new Logger().with(logConfig); +}; + +export default serverLogger; diff --git a/src/middleware.ts b/src/middleware.ts index 10c3ef2..e7494da 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -14,5 +14,5 @@ export function middleware(request: NextRequest) { } export const config = { - matcher: ['/((?!api|static|.*\\..*|_next|favicon.ico|robots.txt).*)'], + matcher: ['/((?!api|static|.*\\..*|_next|favicon.ico|robots.txt|_axiom).*)'], };