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:
parent
ec2f2a31de
commit
23c60cfa45
8 changed files with 137 additions and 7 deletions
5
.changeset/eighty-bobcats-deliver.md
Normal file
5
.changeset/eighty-bobcats-deliver.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@astrojs/vercel': minor
|
||||
---
|
||||
|
||||
Add vercel analytics support
|
|
@ -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[]`
|
||||
|
@ -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';
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
64
packages/integrations/vercel/src/analytics.ts
Normal file
64
packages/integrations/vercel/src/analytics.ts
Normal 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();
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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';
|
||||
},
|
||||
|
|
|
@ -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==}
|
||||
|
||||
|
|
Loading…
Reference in a new issue