0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-16 21:46:22 -05:00

feat(vercel): Add support for analytics (Audiences & Web Vitals) (#6148)

* 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>
This commit is contained in:
Justin Sun 2023-02-09 00:32:20 +08:00 committed by GitHub
parent ec2f2a31de
commit 23c60cfa45
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 137 additions and 7 deletions

View file

@ -0,0 +1,5 @@
---
'@astrojs/vercel': minor
---
Add vercel analytics support

View file

@ -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 Vercels 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[]`
@ -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';
@ -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[]`
@ -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';

View file

@ -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": {
@ -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"

View file

@ -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();
}

View file

@ -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';
@ -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;
@ -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,

View file

@ -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;
@ -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,

View file

@ -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';
},

View file

@ -3335,6 +3335,7 @@ importers:
specifiers:
'@astrojs/webapi': ^2.0.0
'@types/set-cookie-parser': ^2.4.2
'@vercel/analytics': ^0.1.8
'@vercel/nft': ^0.22.1
astro: workspace:*
astro-scripts: workspace:*
@ -3342,11 +3343,14 @@ importers:
fast-glob: ^3.2.11
mocha: ^9.2.2
set-cookie-parser: ^2.5.1
web-vitals: ^3.1.1
dependencies:
'@astrojs/webapi': link:../../webapi
'@vercel/analytics': 0.1.8
'@vercel/nft': 0.22.6
fast-glob: 3.2.12
set-cookie-parser: 2.5.1
web-vitals: 3.1.1
devDependencies:
'@types/set-cookie-parser': 2.4.2
astro: link:../../astro
@ -7623,6 +7627,15 @@ packages:
'@unocss/scope': 0.15.6
dev: false
/@vercel/analytics/0.1.8:
resolution: {integrity: sha512-PQrOI8BJ9qUiVJuQfnKiJd15eDjDJH9TBKsNeMrtelT4NAk7d9mBVz1CoZkvoFnHQ0OW7Xnqmr1F2nScfAnznQ==}
peerDependencies:
react: ^16.8||^17||^18
peerDependenciesMeta:
react:
optional: true
dev: false
/@vercel/nft/0.22.6:
resolution: {integrity: sha512-gTsFnnT4mGxodr4AUlW3/urY+8JKKB452LwF3m477RFUJTAaDmcz2JqFuInzvdybYIeyIv1sSONEJxsxnbQ5JQ==}
engines: {node: '>=14'}
@ -15398,6 +15411,10 @@ packages:
resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==}
engines: {node: '>= 8'}
/web-vitals/3.1.1:
resolution: {integrity: sha512-qvllU+ZeQChqzBhZ1oyXmWsjJ8a2jHYpH8AMaVuf29yscOPZfTQTjQFRX6+eADTdsDE8IanOZ0cetweHMs8/2A==}
dev: false
/webidl-conversions/3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}