Skip to content

Commit

Permalink
feat(vercel): Add support for analytics (Audiences & Web Vitals) (#6148)
Browse files Browse the repository at this point in the history
* feat(intergration/vercel): add vercel analytics support

* docs(intergration/vercel): add vercel analytics prop

* docs(intergration/vercel): bump version to 3.1.0

* Update packages/integrations/vercel/README.md

Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>

* docs(intergration/vercel): add file name for example

* feat(intergration/vercel): convert analytics to ts and support in edge

* docs(intergration/vercel): move file names to code blocks as comments

* fix(intergration/vercel): remove unused import

* feat(intergration/vercel): add analytics support to static mode

* chore(intergration/vercel): revert version change

* style(intergration/vercel): add a blank line after astro import

* chore(intergration/vercel): generate file by changeset

* Update .changeset/eighty-bobcats-deliver.md

Co-authored-by: Chris Swithinbank <swithinbank@gmail.com>

* Update packages/integrations/vercel/README.md

Co-authored-by: Chris Swithinbank <swithinbank@gmail.com>

* Update packages/integrations/vercel/src/analytics.ts

Co-authored-by: Chris Swithinbank <swithinbank@gmail.com>

* chore(intergration/vercel): simplify analytics script

---------

Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>
Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com>
Co-authored-by: Chris Swithinbank <swithinbank@gmail.com>
  • Loading branch information
4 people authored Feb 8, 2023
1 parent ec2f2a3 commit 23c60cf
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/eighty-bobcats-deliver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/vercel': minor
---

Add vercel analytics support
23 changes: 22 additions & 1 deletion packages/integrations/vercel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,26 @@ vercel deploy --prebuilt
To configure this adapter, pass an object to the `vercel()` function call in `astro.config.mjs`:
### analytics
> **Type:** `boolean`
> **Available for:** Serverless, Edge, Static
You can enable [Vercel Analytics](https://vercel.com/analytics) (including Web Vitals and Audiences) by setting `analytics: true`. This will inject Vercel’s tracking scripts into all your pages.
```js
// astro.config.mjs
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel/serverless';
export default defineConfig({
output: 'server',
adapter: vercel({
analytics: true
})
});
```
### includeFiles
> **Type:** `string[]`
Expand All @@ -95,6 +115,7 @@ To configure this adapter, pass an object to the `vercel()` function call in `as
Use this property to force files to be bundled with your function. This is helpful when you notice missing files.
```js
// astro.config.mjs
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel/serverless';
Expand All @@ -109,7 +130,6 @@ export default defineConfig({
> **Note**
> When building for the Edge, all the dependencies get bundled in a single file to save space. **No extra file will be bundled**. So, if you _need_ some file inside the function, you have to specify it in `includeFiles`.
### excludeFiles
> **Type:** `string[]`
Expand All @@ -118,6 +138,7 @@ export default defineConfig({
Use this property to exclude any files from the bundling process that would otherwise be included.
```js
// astro.config.mjs
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel/serverless';
Expand Down
5 changes: 4 additions & 1 deletion packages/integrations/vercel/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"./serverless": "./dist/serverless/adapter.js",
"./serverless/entrypoint": "./dist/serverless/entrypoint.js",
"./static": "./dist/static/adapter.js",
"./analytics": "./dist/analytics.js",
"./package.json": "./package.json"
},
"typesVersions": {
Expand All @@ -45,9 +46,11 @@
},
"dependencies": {
"@astrojs/webapi": "^2.0.0",
"@vercel/analytics": "^0.1.8",
"@vercel/nft": "^0.22.1",
"fast-glob": "^3.2.11",
"set-cookie-parser": "^2.5.1"
"set-cookie-parser": "^2.5.1",
"web-vitals": "^3.1.1"
},
"peerDependencies": {
"astro": "workspace:^2.0.8"
Expand Down
64 changes: 64 additions & 0 deletions packages/integrations/vercel/src/analytics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { inject } from '@vercel/analytics';
import type { Metric } from 'web-vitals';
import { getCLS, getFCP, getFID, getLCP, getTTFB } from 'web-vitals';

const vitalsUrl = 'https://vitals.vercel-analytics.com/v1/vitals';

type Options = { path: string; analyticsId: string };

const getConnectionSpeed = () => {
return 'connection' in navigator &&
navigator['connection'] &&
'effectiveType' in (navigator['connection'] as unknown as { effectiveType: string })
? (navigator['connection'] as unknown as { effectiveType: string })['effectiveType']
: '';
};

const sendToAnalytics = (metric: Metric, options: Options) => {
const body = {
dsn: options.analyticsId,
id: metric.id,
page: options.path,
href: location.href,
event_name: metric.name,
value: metric.value.toString(),
speed: getConnectionSpeed(),
};
const blob = new Blob([new URLSearchParams(body).toString()], {
type: 'application/x-www-form-urlencoded',
});
if (navigator.sendBeacon) {
navigator.sendBeacon(vitalsUrl, blob);
} else
fetch(vitalsUrl, {
body: blob,
method: 'POST',
credentials: 'omit',
keepalive: true,
});
};

function webVitals() {
const analyticsId = (import.meta as any).env.PUBLIC_VERCEL_ANALYTICS_ID;
if (!analyticsId) {
console.error('[Analytics] VERCEL_ANALYTICS_ID not found');
return;
}
const options: Options = { path: window.location.pathname, analyticsId };
try {
getFID((metric) => sendToAnalytics(metric, options));
getTTFB((metric) => sendToAnalytics(metric, options));
getLCP((metric) => sendToAnalytics(metric, options));
getCLS((metric) => sendToAnalytics(metric, options));
getFCP((metric) => sendToAnalytics(metric, options));
} catch (err) {
console.error('[Analytics]', err);
}
}

const mode = (import.meta as any).env.MODE as 'development' | 'production';

inject({ mode });
if (mode === 'production') {
webVitals();
}
12 changes: 10 additions & 2 deletions packages/integrations/vercel/src/edge/adapter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { AstroAdapter, AstroConfig, AstroIntegration } from 'astro';

import esbuild from 'esbuild';
import { relative as relativePath } from 'node:path';
import { fileURLToPath } from 'node:url';
Expand All @@ -24,9 +25,13 @@ function getAdapter(): AstroAdapter {

export interface VercelEdgeConfig {
includeFiles?: string[];
analytics?: boolean;
}

export default function vercelEdge({ includeFiles = [] }: VercelEdgeConfig = {}): AstroIntegration {
export default function vercelEdge({
includeFiles = [],
analytics,
}: VercelEdgeConfig = {}): AstroIntegration {
let _config: AstroConfig;
let buildTempFolder: URL;
let functionFolder: URL;
Expand All @@ -35,7 +40,10 @@ export default function vercelEdge({ includeFiles = [] }: VercelEdgeConfig = {})
return {
name: PACKAGE_NAME,
hooks: {
'astro:config:setup': ({ config, updateConfig }) => {
'astro:config:setup': ({ config, updateConfig, injectScript }) => {
if (analytics) {
injectScript('page', 'import "@astrojs/vercel/analytics"');
}
const outDir = getVercelOutput(config.root);
updateConfig({
outDir,
Expand Down
7 changes: 6 additions & 1 deletion packages/integrations/vercel/src/serverless/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ function getAdapter(): AstroAdapter {
export interface VercelServerlessConfig {
includeFiles?: string[];
excludeFiles?: string[];
analytics?: boolean;
}

export default function vercelServerless({
includeFiles,
excludeFiles,
analytics,
}: VercelServerlessConfig = {}): AstroIntegration {
let _config: AstroConfig;
let buildTempFolder: URL;
Expand All @@ -33,7 +35,10 @@ export default function vercelServerless({
return {
name: PACKAGE_NAME,
hooks: {
'astro:config:setup': ({ config, updateConfig }) => {
'astro:config:setup': ({ config, updateConfig, injectScript }) => {
if (analytics) {
injectScript('page', 'import "@astrojs/vercel/analytics"');
}
const outDir = getVercelOutput(config.root);
updateConfig({
outDir,
Expand Down
11 changes: 9 additions & 2 deletions packages/integrations/vercel/src/static/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,20 @@ function getAdapter(): AstroAdapter {
return { name: PACKAGE_NAME };
}

export default function vercelStatic(): AstroIntegration {
export interface VercelStaticConfig {
analytics?: boolean;
}

export default function vercelStatic({ analytics }: VercelStaticConfig = {}): AstroIntegration {
let _config: AstroConfig;

return {
name: '@astrojs/vercel',
hooks: {
'astro:config:setup': ({ config }) => {
'astro:config:setup': ({ config, injectScript }) => {
if (analytics) {
injectScript('page', 'import "@astrojs/vercel/analytics"');
}
config.outDir = new URL('./static/', getVercelOutput(config.root));
config.build.format = 'directory';
},
Expand Down
17 changes: 17 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 23c60cf

Please sign in to comment.