summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Justin Sun <i@jsun969.cn> 2023-02-09 00:32:20 +0800
committerGravatar GitHub <noreply@github.com> 2023-02-08 13:32:20 -0300
commit23c60cfa45d0c01c2a710de9c6a644cd91d1b3f3 (patch)
treef813e8cd50ee4959e37dd8734102c273a289c060
parentec2f2a31dec78e5749cdea524ae926a19df300e3 (diff)
downloadastro-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>
-rw-r--r--.changeset/eighty-bobcats-deliver.md5
-rw-r--r--packages/integrations/vercel/README.md23
-rw-r--r--packages/integrations/vercel/package.json5
-rw-r--r--packages/integrations/vercel/src/analytics.ts64
-rw-r--r--packages/integrations/vercel/src/edge/adapter.ts12
-rw-r--r--packages/integrations/vercel/src/serverless/adapter.ts7
-rw-r--r--packages/integrations/vercel/src/static/adapter.ts11
-rw-r--r--pnpm-lock.yaml17
8 files changed, 137 insertions, 7 deletions
diff --git a/.changeset/eighty-bobcats-deliver.md b/.changeset/eighty-bobcats-deliver.md
new file mode 100644
index 000000000..064719c78
--- /dev/null
+++ b/.changeset/eighty-bobcats-deliver.md
@@ -0,0 +1,5 @@
+---
+'@astrojs/vercel': minor
+---
+
+Add vercel analytics support
diff --git a/packages/integrations/vercel/README.md b/packages/integrations/vercel/README.md
index db89064c8..64bd66a89 100644
--- a/packages/integrations/vercel/README.md
+++ b/packages/integrations/vercel/README.md
@@ -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';
diff --git a/packages/integrations/vercel/package.json b/packages/integrations/vercel/package.json
index 95908173b..8332c64dd 100644
--- a/packages/integrations/vercel/package.json
+++ b/packages/integrations/vercel/package.json
@@ -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"
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';
},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 73a3bfe82..cd3ab9c87 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -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==}