diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 0a0b4d6..5ffea73 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -10,28 +10,40 @@ concurrency: cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} jobs: - typescript: - name: typescript + biome: + name: lint and format + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + persist-credentials: false + - name: Setup Biome + uses: biomejs/setup-biome@v2 + with: + version: latest + - name: Run Biome + run: biome ci + + tests: + name: unit tests runs-on: ubuntu-latest - timeout-minutes: 10 steps: - - name: checkout - uses: actions/checkout@v3 + - name: Checkout + uses: actions/checkout@v5 + with: + persist-credentials: false - name: Install uses: ./.github/composite-actions/install - - name: lint - run: pnpm -r lint - - name: type-check - run: pnpm -r type-check - - name: prettier - run: pnpm prettier -c . - - name: build - run: pnpm -r build - - name: test + - name: Build + run: pnpm -F @vercel/analytics build + - name: Run tests run: pnpm -r test playwright: - name: playwright + name: e2e tests runs-on: ubuntu-latest timeout-minutes: 10 strategy: @@ -41,14 +53,14 @@ jobs: - 'test:e2e:production' - 'test:e2e:development' steps: - - name: checkout - uses: actions/checkout@v3 + - name: Checkout + uses: actions/checkout@v5 - name: Install uses: ./.github/composite-actions/install - - name: get playwright version + - name: Get Playwright version id: pw run: echo "version=$(cat apps/nextjs/package.json | jq -r '.devDependencies."@playwright/test"')" >> $GITHUB_OUTPUT - - name: install playwright + - name: Install Playwright run: pnpx playwright@${{ steps.pw.outputs.version }} install - - name: test + - name: Run tests run: pnpm -r ${{ matrix.test }} diff --git a/.gitignore b/.gitignore index ab0c11c..21efa56 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ dist .DS_Store .vscode .vercel +.claude diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index a5a29d9..0000000 --- a/.husky/pre-commit +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - -pnpm lint-staged diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 767c4a8..0000000 --- a/.prettierignore +++ /dev/null @@ -1,10 +0,0 @@ -.next/ -dist/ -.astro/ -.vercel/ -.nuxt/ -.output/ -.cache/ -build/ -.svelte-kit/ -pnpm-lock.yaml \ No newline at end of file diff --git a/apps/astro/src/pages/blog/[...slug].astro b/apps/astro/src/pages/blog/[...slug].astro index 0945e4e..c22895d 100644 --- a/apps/astro/src/pages/blog/[...slug].astro +++ b/apps/astro/src/pages/blog/[...slug].astro @@ -4,8 +4,9 @@ import Layout from '../../layouts/Base.astro'; export async function getStaticPaths() { const blogEntries = await getCollection('blog'); - return blogEntries.map(entry => ({ - params: { slug: entry.slug }, props: { entry }, + return blogEntries.map((entry) => ({ + params: { slug: entry.slug }, + props: { entry }, })); } diff --git a/apps/nextjs-15/src/app/layout.tsx b/apps/nextjs-15/src/app/layout.tsx index b4c4311..af44dfc 100644 --- a/apps/nextjs-15/src/app/layout.tsx +++ b/apps/nextjs-15/src/app/layout.tsx @@ -1,6 +1,6 @@ +import { Analytics } from '@vercel/analytics/next'; import type { Metadata } from 'next'; import localFont from 'next/font/local'; -import { Analytics } from '@vercel/analytics/next'; import './globals.css'; const geistSans = localFont({ diff --git a/apps/nextjs-15/src/app/page.module.css b/apps/nextjs-15/src/app/page.module.css index 8a46041..ee9b8e6 100644 --- a/apps/nextjs-15/src/app/page.module.css +++ b/apps/nextjs-15/src/app/page.module.css @@ -68,7 +68,10 @@ padding: 0 20px; border: none; border: 1px solid transparent; - transition: background 0.2s, color 0.2s, border-color 0.2s; + transition: + background 0.2s, + color 0.2s, + border-color 0.2s; cursor: pointer; display: flex; align-items: center; diff --git a/apps/nextjs/app/api/edge-no-context/route.test.ts b/apps/nextjs/app/api/edge-no-context/route.test.ts index 051f05b..f008e26 100644 --- a/apps/nextjs/app/api/edge-no-context/route.test.ts +++ b/apps/nextjs/app/api/edge-no-context/route.test.ts @@ -10,7 +10,7 @@ describe('app API edge-no-context route', () => { expect(response.status).toBe(200); expect(log).toHaveBeenCalledTimes(1); expect(log).toHaveBeenCalledWith( - '[Vercel Web Analytics] Track "Edge Event" with data {"data":"edge","router":"app","manual":true}' + '[Vercel Web Analytics] Track "Edge Event" with data {"data":"edge","router":"app","manual":true}', ); }); }); diff --git a/apps/nextjs/app/api/edge-no-context/route.ts b/apps/nextjs/app/api/edge-no-context/route.ts index 898647c..b81edc9 100644 --- a/apps/nextjs/app/api/edge-no-context/route.ts +++ b/apps/nextjs/app/api/edge-no-context/route.ts @@ -12,7 +12,7 @@ async function handler(request: Request) { }, { request, - } + }, ); return new Response('OK'); diff --git a/apps/nextjs/app/api/edge/route.test.ts b/apps/nextjs/app/api/edge/route.test.ts index 5b3f2e2..c2e6d62 100644 --- a/apps/nextjs/app/api/edge/route.test.ts +++ b/apps/nextjs/app/api/edge/route.test.ts @@ -10,7 +10,7 @@ describe('app API edge route', () => { expect(response.status).toBe(200); expect(log).toHaveBeenCalledTimes(1); expect(log).toHaveBeenCalledWith( - '[Vercel Web Analytics] Track "Edge Event" with data {"data":"edge","router":"app"}' + '[Vercel Web Analytics] Track "Edge Event" with data {"data":"edge","router":"app"}', ); }); }); diff --git a/apps/nextjs/app/api/serverless/route.test.ts b/apps/nextjs/app/api/serverless/route.test.ts index 5a9eded..9bc1a18 100644 --- a/apps/nextjs/app/api/serverless/route.test.ts +++ b/apps/nextjs/app/api/serverless/route.test.ts @@ -10,7 +10,7 @@ describe('app API serverless route', () => { expect(response.status).toBe(200); expect(log).toHaveBeenCalledTimes(1); expect(log).toHaveBeenCalledWith( - '[Vercel Web Analytics] Track "Serverless Event" with data {"data":"serverless","router":"app"}' + '[Vercel Web Analytics] Track "Serverless Event" with data {"data":"serverless","router":"app"}', ); }); }); diff --git a/apps/nextjs/app/rsc/page.tsx b/apps/nextjs/app/rsc/page.tsx index 1e1d839..21a1fc9 100644 --- a/apps/nextjs/app/rsc/page.tsx +++ b/apps/nextjs/app/rsc/page.tsx @@ -1,5 +1,5 @@ -import { cookies } from 'next/headers'; import { track } from '@vercel/analytics/server'; +import { cookies } from 'next/headers'; export default async function RSC() { cookies(); diff --git a/apps/nextjs/e2e/development/beforeSend.spec.ts b/apps/nextjs/e2e/development/beforeSend.spec.ts index 8ba5fea..5d59d45 100644 --- a/apps/nextjs/e2e/development/beforeSend.spec.ts +++ b/apps/nextjs/e2e/development/beforeSend.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@playwright/test'; +import { expect, test } from '@playwright/test'; import { useMockForProductionScript } from '../utils'; test.describe('beforeSend', () => { @@ -35,15 +35,15 @@ test.describe('beforeSend', () => { expect( messages.find((m) => - m.includes('[pageview] http://localhost:3000/before-send/first') - ) + m.includes('[pageview] http://localhost:3000/before-send/first'), + ), ).toBeDefined(); expect( messages.find((m) => m.includes( - '[pageview] http://localhost:3000/before-send/second?secret=REDACTED' - ) - ) + '[pageview] http://localhost:3000/before-send/second?secret=REDACTED', + ), + ), ).toBeDefined(); expect(messages.find((m) => m.includes('secret=vercel'))).toBeUndefined(); diff --git a/apps/nextjs/e2e/development/pageview.spec.ts b/apps/nextjs/e2e/development/pageview.spec.ts index e585707..5ac69d8 100644 --- a/apps/nextjs/e2e/development/pageview.spec.ts +++ b/apps/nextjs/e2e/development/pageview.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@playwright/test'; +import { expect, test } from '@playwright/test'; import { useMockForProductionScript } from '../utils'; test.describe('pageview', () => { @@ -35,13 +35,13 @@ test.describe('pageview', () => { expect( messages.find((m) => - m.includes('[pageview] http://localhost:3000/navigation/first') - ) + m.includes('[pageview] http://localhost:3000/navigation/first'), + ), ).toBeDefined(); expect( messages.find((m) => - m.includes('[pageview] http://localhost:3000/navigation/second') - ) + m.includes('[pageview] http://localhost:3000/navigation/second'), + ), ).toBeDefined(); }); }); diff --git a/apps/nextjs/e2e/production/beforeSend.spec.ts b/apps/nextjs/e2e/production/beforeSend.spec.ts index 03bc806..31d4f71 100644 --- a/apps/nextjs/e2e/production/beforeSend.spec.ts +++ b/apps/nextjs/e2e/production/beforeSend.spec.ts @@ -1,11 +1,11 @@ -import { test, expect } from '@playwright/test'; +import { expect, test } from '@playwright/test'; import { useMockForProductionScript } from '../utils'; test.describe('beforeSend', () => { test('should replace the value of the secret query parameter', async ({ page, }) => { - const payloads: { page: string; payload: Object }[] = []; + const payloads: { page: string; payload: unknown }[] = []; await useMockForProductionScript({ page, diff --git a/apps/nextjs/e2e/production/pageview.spec.ts b/apps/nextjs/e2e/production/pageview.spec.ts index 7eb25c1..5ec9461 100644 --- a/apps/nextjs/e2e/production/pageview.spec.ts +++ b/apps/nextjs/e2e/production/pageview.spec.ts @@ -1,11 +1,11 @@ -import { test, expect } from '@playwright/test'; +import { expect, test } from '@playwright/test'; import { useMockForProductionScript } from '../utils'; test.describe('pageview', () => { test('should track page views when navigating between pages', async ({ page, }) => { - const payloads: { page: string; payload: Object }[] = []; + const payloads: { page: string; payload: unknown }[] = []; await useMockForProductionScript({ page, @@ -52,7 +52,7 @@ test.describe('pageview', () => { }); test('should properly send dynamic route', async ({ page }) => { - const payloads: { page: string; payload: Object }[] = []; + const payloads: { page: string; payload: unknown }[] = []; await useMockForProductionScript({ page, @@ -132,7 +132,7 @@ test.describe('pageview', () => { test('should send pageviews when route doesnt change but path does', async ({ page, }) => { - const payloads: { page: string; payload: Object }[] = []; + const payloads: { page: string; payload: unknown }[] = []; await useMockForProductionScript({ page, diff --git a/apps/nextjs/e2e/utils.ts b/apps/nextjs/e2e/utils.ts index 9aeeaf5..36044ed 100644 --- a/apps/nextjs/e2e/utils.ts +++ b/apps/nextjs/e2e/utils.ts @@ -1,10 +1,15 @@ -import { Page } from '@playwright/test'; +import type { Page } from '@playwright/test'; export async function useMockForProductionScript(props: { page: Page; - onPageView: (page: string, payload: Object) => void; + onPageView: (page: string, payload: unknown) => void; debug?: boolean; }) { + await props.page.addInitScript({ + content: + "Object.defineProperty(navigator, 'webdriver', { get() { return undefined }})", + }); + await props.page.route('**/_vercel/insights/script.js', async (route, _) => { return route.fulfill({ status: 301, diff --git a/apps/nextjs/middleware.ts b/apps/nextjs/middleware.ts index c37846f..e16578f 100644 --- a/apps/nextjs/middleware.ts +++ b/apps/nextjs/middleware.ts @@ -1,13 +1,13 @@ -import { NextResponse } from 'next/server'; -import type { NextFetchEvent, NextRequest } from 'next/server'; import { track } from '@vercel/analytics/server'; +import type { NextFetchEvent, NextRequest } from 'next/server'; +import { NextResponse } from 'next/server'; export async function middleware(request: NextRequest, event: NextFetchEvent) { event.waitUntil( track('Redirect', { path: request.nextUrl.pathname, type: 'waitUntil', - }) + }), ); return NextResponse.redirect(new URL('/server-actions', request.url)); } diff --git a/apps/nextjs/pages/api/test.test.ts b/apps/nextjs/pages/api/test.test.ts index 9009f94..d2074b1 100644 --- a/apps/nextjs/pages/api/test.test.ts +++ b/apps/nextjs/pages/api/test.test.ts @@ -13,7 +13,7 @@ describe('pages API edge route', () => { expect(response.status).toBe(200); expect(log).toHaveBeenCalledTimes(1); expect(log).toHaveBeenCalledWith( - '[Vercel Web Analytics] Track "Pages Api Route" with data {"runtime":"edge","router":"pages"}' + '[Vercel Web Analytics] Track "Pages Api Route" with data {"runtime":"edge","router":"pages"}', ); }); }); diff --git a/apps/nextjs/pages/api/test.ts b/apps/nextjs/pages/api/test.ts index cc4af1d..d60b275 100644 --- a/apps/nextjs/pages/api/test.ts +++ b/apps/nextjs/pages/api/test.ts @@ -1,11 +1,11 @@ import { track } from '@vercel/analytics/server'; -import { NextFetchEvent, NextRequest } from 'next/server'; +import type { NextFetchEvent, NextRequest } from 'next/server'; export const config = { runtime: 'edge', }; -async function handler(request: NextRequest, event: NextFetchEvent) { +async function handler(_request: NextRequest, _event: NextFetchEvent) { track('Pages Api Route', { runtime: 'edge', router: 'pages', diff --git a/apps/nextjs/playwright.config.ts b/apps/nextjs/playwright.config.ts index 0b195f7..d592642 100644 --- a/apps/nextjs/playwright.config.ts +++ b/apps/nextjs/playwright.config.ts @@ -1,5 +1,5 @@ -import { PlaywrightTestConfig, devices } from '@playwright/test'; -import path from 'path'; +import path from 'node:path'; +import { devices, type PlaywrightTestConfig } from '@playwright/test'; // Use process.env.PORT by default and fallback to port 3000 const PORT = process.env.PORT || 3000; diff --git a/apps/nuxt/app/assets/base.css b/apps/nuxt/app/assets/base.css index fc15cc4..32a1bdb 100644 --- a/apps/nuxt/app/assets/base.css +++ b/apps/nuxt/app/assets/base.css @@ -62,11 +62,13 @@ body { min-height: 100vh; color: var(--color-text); background: var(--color-background); - transition: color 0.5s, background-color 0.5s; + transition: + color 0.5s, + background-color 0.5s; line-height: 1.6; - font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, - Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; + font-family: + Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, + Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 15px; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; diff --git a/apps/nuxt/app/assets/main.css b/apps/nuxt/app/assets/main.css index 9db9703..0281132 100644 --- a/apps/nuxt/app/assets/main.css +++ b/apps/nuxt/app/assets/main.css @@ -1,4 +1,4 @@ -@import './base.css'; +@import "./base.css"; #app { max-width: 1280px; diff --git a/apps/sveltekit/src/routes/+layout.ts b/apps/sveltekit/src/routes/+layout.ts index f139d1e..0275424 100644 --- a/apps/sveltekit/src/routes/+layout.ts +++ b/apps/sveltekit/src/routes/+layout.ts @@ -1,8 +1,8 @@ -import { dev } from '$app/environment'; import { - injectAnalytics, type BeforeSendEvent, + injectAnalytics, } from '@vercel/analytics/sveltekit'; +import { dev } from '$app/environment'; injectAnalytics({ mode: dev ? 'development' : 'production', diff --git a/apps/sveltekit/src/routes/[slug]/+page.svelte b/apps/sveltekit/src/routes/[slug]/+page.svelte index 665757c..682d5de 100644 --- a/apps/sveltekit/src/routes/[slug]/+page.svelte +++ b/apps/sveltekit/src/routes/[slug]/+page.svelte @@ -1,6 +1,6 @@