diff options
author | 2023-02-09 00:32:20 +0800 | |
---|---|---|
committer | 2023-02-08 13:32:20 -0300 | |
commit | 23c60cfa45d0c01c2a710de9c6a644cd91d1b3f3 (patch) | |
tree | f813e8cd50ee4959e37dd8734102c273a289c060 /packages/integrations/vercel/src | |
parent | ec2f2a31dec78e5749cdea524ae926a19df300e3 (diff) | |
download | astro-23c60cfa45d0c01c2a710de9c6a644cd91d1b3f3.tar.gz astro-23c60cfa45d0c01c2a710de9c6a644cd91d1b3f3.tar.zst astro-23c60cfa45d0c01c2a710de9c6a644cd91d1b3f3.zip |
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>
Diffstat (limited to 'packages/integrations/vercel/src')
-rw-r--r-- | packages/integrations/vercel/src/analytics.ts | 64 | ||||
-rw-r--r-- | packages/integrations/vercel/src/edge/adapter.ts | 12 | ||||
-rw-r--r-- | packages/integrations/vercel/src/serverless/adapter.ts | 7 | ||||
-rw-r--r-- | packages/integrations/vercel/src/static/adapter.ts | 11 |
4 files changed, 89 insertions, 5 deletions
diff --git a/packages/integrations/vercel/src/analytics.ts b/packages/integrations/vercel/src/analytics.ts new file mode 100644 index 000000000..95dee83e3 --- /dev/null +++ b/packages/integrations/vercel/src/analytics.ts @@ -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(); +} diff --git a/packages/integrations/vercel/src/edge/adapter.ts b/packages/integrations/vercel/src/edge/adapter.ts index 3f38a074e..3e9eb5929 100644 --- a/packages/integrations/vercel/src/edge/adapter.ts +++ b/packages/integrations/vercel/src/edge/adapter.ts @@ -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, diff --git a/packages/integrations/vercel/src/serverless/adapter.ts b/packages/integrations/vercel/src/serverless/adapter.ts index 3ff5eb3e5..884510516 100644 --- a/packages/integrations/vercel/src/serverless/adapter.ts +++ b/packages/integrations/vercel/src/serverless/adapter.ts @@ -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, diff --git a/packages/integrations/vercel/src/static/adapter.ts b/packages/integrations/vercel/src/static/adapter.ts index 597c93626..28da5d4da 100644 --- a/packages/integrations/vercel/src/static/adapter.ts +++ b/packages/integrations/vercel/src/static/adapter.ts @@ -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'; }, |