Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[@astrojs/vercel] Individually enable Speed Insights and Web Analytics #8021

Merged
merged 29 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4a7bad4
Individually enable Speed Insights and Web Analytics
chriswdmr Aug 10, 2023
3db8381
Update pnpm-lock.yaml
chriswdmr Aug 10, 2023
6b4fc91
Remove .only on tests
chriswdmr Aug 10, 2023
905a2a9
Merge remote-tracking branch 'upstream/main'
chriswdmr Aug 28, 2023
e12538a
Fix build
chriswdmr Aug 29, 2023
2e11ca5
Merge branch 'withastro:main' into main
chriswdmr Aug 29, 2023
97511dd
Move `beforeSend` out of config
chriswdmr Aug 29, 2023
30003d7
Address feedback from review
chriswdmr Aug 29, 2023
6d91119
Update README.md
chriswdmr Aug 29, 2023
99b07d4
Add back the `analytics` property and add deprecation warning when used
chriswdmr Aug 29, 2023
93beaf9
Add migration guide for the deprecated `analytics` property
chriswdmr Aug 29, 2023
9598759
Merge branch 'main' into main
chriswdmr Aug 30, 2023
5494466
Merge branch 'main' into main
chriswdmr Sep 4, 2023
05df84f
Update packages/integrations/vercel/README.md
chriswdmr Sep 4, 2023
704640a
Update README.md
chriswdmr Sep 4, 2023
a8d0e77
Merge branch 'main' into main
chriswdmr Sep 4, 2023
2608fee
Merge branch 'main' into main
chriswdmr Sep 4, 2023
d0cf8c7
Merge branch 'main' into main
chriswdmr Sep 5, 2023
45c522f
Merge branch 'main' into main
chriswdmr Sep 5, 2023
ad86e9f
Merge branch 'main' into main
matthewp Sep 5, 2023
e492e94
Fix external dependency issue
chriswdmr Sep 6, 2023
09de89b
Merge branch 'main' into main
chriswdmr Sep 6, 2023
a44d8bb
Simplify plugin and reduce scope
chriswdmr Sep 7, 2023
88cf8e1
Update .changeset/sixty-teachers-tap.md
chriswdmr Sep 12, 2023
4441016
Apply feedback from review
chriswdmr Sep 12, 2023
f34dcc9
Merge remote-tracking branch 'upstream/main'
chriswdmr Sep 12, 2023
bf986d5
Move exposeEnv to speed-insights since it's only used there
chriswdmr Sep 13, 2023
0a63721
Merge branch 'main' into main
chriswdmr Sep 13, 2023
1dae36e
Merge branch 'main' into main
chriswdmr Sep 14, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .changeset/sixty-teachers-tap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
'@astrojs/vercel': minor
---

Enable Vercel Speed Insights and Vercel Web Analytics individually.
Deprecates the `analytics` property in `astro.config.mjs` in favor of `speedInsights` and `webAnalytics`.

If you're using the `analytics` property, you'll need to update your config to use the new properties:

```diff
// astro.config.mjs
export default defineConfig({
adapter: vercel({
- analytics: true,
+ webAnalytics: {
+ enabled: true
+ },
+ speedInsights: {
+ enabled: true
+ }
})
});
```

Allow configuration of Web Analytics with all available configuration options.
Bumps @vercel/analytics package to the latest version.
37 changes: 31 additions & 6 deletions packages/integrations/vercel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,36 @@ vercel deploy --prebuilt

To configure this adapter, pass an object to the `vercel()` function call in `astro.config.mjs`:

### analytics
### Web Analytics

**Type:** `boolean`<br>
**Available for:** Serverless, Static<br>
**Added in:** `@astrojs/vercel@3.1.0`
**Type:** `VercelWebAnalyticsConfig`<br>
**Available for:** Serverless, Edge, Static<br>
**Added in:** `@astrojs/vercel@3.8.0`

You can enable [Vercel Web Analytics](https://vercel.com/docs/concepts/analytics) by setting `webAnalytics: { enabled: true }`. This will inject Vercel’s tracking scripts into all of your pages.

```js
// astro.config.mjs
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel/serverless';

export default defineConfig({
output: 'server',
adapter: vercel({
webAnalytics: {
enabled: true,
},
}),
});
```

### Speed Insights

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.
You can enable [Vercel Speed Insights](https://vercel.com/docs/concepts/speed-insights) by setting `speedInsights: { enabled: true }`. This will collect and send Web Vital data to Vercel.

**Type:** `VercelSpeedInsightsConfig`<br>
**Available for:** Serverless, Edge, Static<br>
**Added in:** `@astrojs/vercel@3.8.0`

```js
// astro.config.mjs
Expand All @@ -101,7 +124,9 @@ import vercel from '@astrojs/vercel/serverless';
export default defineConfig({
output: 'server',
adapter: vercel({
analytics: true,
speedInsights: {
enabled: true,
},
}),
});
```
Expand Down
2 changes: 1 addition & 1 deletion packages/integrations/vercel/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"./serverless": "./dist/serverless/adapter.js",
"./serverless/entrypoint": "./dist/serverless/entrypoint.js",
"./static": "./dist/static/adapter.js",
"./analytics": "./dist/analytics.js",
"./speed-insights": "./dist/speed-insights.js",
"./build-image-service": "./dist/image/build-service.js",
"./dev-image-service": "./dist/image/dev-service.js",
"./squoosh-dev-service": "./dist/image/squoosh-dev-service.js",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
export type VercelSpeedInsightsConfig = {
enabled: boolean;
};

export function getSpeedInsightsViteConfig(enabled?: boolean) {
if (enabled) {
return {
define: exposeEnv(['VERCEL_ANALYTICS_ID']),
};
}

return {};
}

/**
* While Vercel adds the `PUBLIC_` prefix for their `VERCEL_` env vars by default, some env vars
* like `VERCEL_ANALYTICS_ID` aren't, so handle them here so that it works correctly in runtime.
Expand Down
30 changes: 30 additions & 0 deletions packages/integrations/vercel/src/lib/web-analytics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export type VercelWebAnalyticsConfig = {
enabled: boolean;
};

export async function getInjectableWebAnalyticsContent({
mode,
}: {
mode: 'development' | 'production';
}) {
const base = `window.va = window.va || function () { (window.vaq = window.vaq || []).push(arguments); };`;

if (mode === 'development') {
return `
${base}
var script = document.createElement('script');
script.defer = true;
script.src = 'https://cdn.vercel-insights.com/v1/script.debug.js';
var head = document.querySelector('head');
head.appendChild(script);
`;
}

return `${base}
var script = document.createElement('script');
script.defer = true;
script.src = '/_vercel/insights/script.js';
var head = document.querySelector('head');
head.appendChild(script);
`;
}
43 changes: 35 additions & 8 deletions packages/integrations/vercel/src/serverless/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,17 @@ import {
type DevImageService,
type VercelImageConfig,
} from '../image/shared.js';
import { exposeEnv } from '../lib/env.js';
import { getVercelOutput, removeDir, writeJson } from '../lib/fs.js';
import { copyDependenciesToFunction } from '../lib/nft.js';
import { getRedirects } from '../lib/redirects.js';
import {
getSpeedInsightsViteConfig,
type VercelSpeedInsightsConfig,
} from '../lib/speed-insights.js';
import {
getInjectableWebAnalyticsContent,
type VercelWebAnalyticsConfig,
} from '../lib/web-analytics.js';
import { generateEdgeMiddleware } from './middleware.js';

const PACKAGE_NAME = '@astrojs/vercel/serverless';
Expand Down Expand Up @@ -64,9 +71,14 @@ function getAdapter({
}

export interface VercelServerlessConfig {
/**
* @deprecated
*/
analytics?: boolean;
webAnalytics?: VercelWebAnalyticsConfig;
speedInsights?: VercelSpeedInsightsConfig;
includeFiles?: string[];
excludeFiles?: string[];
analytics?: boolean;
imageService?: boolean;
imagesConfig?: VercelImageConfig;
devImageService?: DevImageService;
Expand All @@ -75,9 +87,11 @@ export interface VercelServerlessConfig {
}

export default function vercelServerless({
analytics,
webAnalytics,
speedInsights,
includeFiles,
excludeFiles,
analytics,
imageService,
imagesConfig,
devImageService = 'sharp',
Expand Down Expand Up @@ -131,12 +145,25 @@ export default function vercelServerless({
return {
name: PACKAGE_NAME,
hooks: {
'astro:config:setup': ({ command, config, updateConfig, injectScript }) => {
if (command === 'build' && analytics) {
injectScript('page', 'import "@astrojs/vercel/analytics"');
'astro:config:setup': async ({ command, config, updateConfig, injectScript, logger }) => {
if (webAnalytics?.enabled || analytics) {
if (analytics) {
logger.warn(
`The \`analytics\` property is deprecated. Please use the new \`webAnalytics\` and \`speedInsights\` properties instead.`
);
}

injectScript(
'head-inline',
await getInjectableWebAnalyticsContent({
mode: command === 'dev' ? 'development' : 'production',
})
);
}
if (command === 'build' && (speedInsights?.enabled || analytics)) {
injectScript('page', 'import "@astrojs/vercel/speed-insights"');
chriswdmr marked this conversation as resolved.
Show resolved Hide resolved
}
const outDir = getVercelOutput(config.root);
const viteDefine = exposeEnv(['VERCEL_ANALYTICS_ID']);
updateConfig({
outDir,
build: {
Expand All @@ -145,7 +172,7 @@ export default function vercelServerless({
server: new URL('./dist/', config.root),
},
vite: {
define: viteDefine,
...getSpeedInsightsViteConfig(speedInsights?.enabled || analytics),
ssr: {
external: ['@vercel/nft'],
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { inject } from '@vercel/analytics';
import type { Metric } from 'web-vitals';
import { getCLS, getFCP, getFID, getLCP, getTTFB } from 'web-vitals';
import { onCLS, onFCP, onFID, onLCP, onTTFB } from 'web-vitals';

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

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

Expand All @@ -14,7 +13,7 @@ const getConnectionSpeed = () => {
: '';
};

const sendToAnalytics = (metric: Metric, options: Options) => {
const sendToSpeedInsights = (metric: Metric, options: Options) => {
const body = {
dsn: options.analyticsId,
id: metric.id,
Expand All @@ -28,37 +27,39 @@ const sendToAnalytics = (metric: Metric, options: Options) => {
type: 'application/x-www-form-urlencoded',
});
if (navigator.sendBeacon) {
navigator.sendBeacon(vitalsUrl, blob);
navigator.sendBeacon(SPEED_INSIGHTS_INTAKE, blob);
} else
fetch(vitalsUrl, {
fetch(SPEED_INSIGHTS_INTAKE, {
body: blob,
method: 'POST',
credentials: 'omit',
keepalive: true,
});
};

function webVitals() {
function collectWebVitals() {
const analyticsId = (import.meta as any).env.PUBLIC_VERCEL_ANALYTICS_ID;

if (!analyticsId) {
console.error('[Analytics] VERCEL_ANALYTICS_ID not found');
console.error('[Speed Insights] 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));
onFID((metric) => sendToSpeedInsights(metric, options));
onTTFB((metric) => sendToSpeedInsights(metric, options));
onLCP((metric) => sendToSpeedInsights(metric, options));
onCLS((metric) => sendToSpeedInsights(metric, options));
onFCP((metric) => sendToSpeedInsights(metric, options));
} catch (err) {
console.error('[Analytics]', err);
console.error('[Speed Insights]', err);
}
}

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

inject({ mode });
if (mode === 'production') {
webVitals();
collectWebVitals();
}
39 changes: 33 additions & 6 deletions packages/integrations/vercel/src/static/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,17 @@ import {
type DevImageService,
type VercelImageConfig,
} from '../image/shared.js';
import { exposeEnv } from '../lib/env.js';
import { emptyDir, getVercelOutput, writeJson } from '../lib/fs.js';
import { isServerLikeOutput } from '../lib/prerender.js';
import { getRedirects } from '../lib/redirects.js';
import {
getSpeedInsightsViteConfig,
type VercelSpeedInsightsConfig,
} from '../lib/speed-insights.js';
import {
getInjectableWebAnalyticsContent,
type VercelWebAnalyticsConfig,
} from '../lib/web-analytics.js';

const PACKAGE_NAME = '@astrojs/vercel/static';

Expand All @@ -34,14 +41,21 @@ function getAdapter(): AstroAdapter {
}

export interface VercelStaticConfig {
/**
* @deprecated
*/
analytics?: boolean;
webAnalytics?: VercelWebAnalyticsConfig;
speedInsights?: VercelSpeedInsightsConfig;
imageService?: boolean;
imagesConfig?: VercelImageConfig;
devImageService?: DevImageService;
}

export default function vercelStatic({
analytics,
webAnalytics,
speedInsights,
imageService,
imagesConfig,
devImageService = 'sharp',
Expand All @@ -51,20 +65,33 @@ export default function vercelStatic({
return {
name: '@astrojs/vercel',
hooks: {
'astro:config:setup': ({ command, config, injectScript, updateConfig }) => {
if (command === 'build' && analytics) {
injectScript('page', 'import "@astrojs/vercel/analytics"');
'astro:config:setup': async ({ command, config, injectScript, updateConfig, logger }) => {
if (webAnalytics?.enabled || analytics) {
if (analytics) {
logger.warn(
`The \`analytics\` property is deprecated. Please use the new \`webAnalytics\` and \`speedInsights\` properties instead.`
);
}

injectScript(
'head-inline',
await getInjectableWebAnalyticsContent({
mode: command === 'dev' ? 'development' : 'production',
})
);
}
if (command === 'build' && (speedInsights?.enabled || analytics)) {
injectScript('page', 'import "@astrojs/vercel/speed-insights"');
}
const outDir = new URL('./static/', getVercelOutput(config.root));
const viteDefine = exposeEnv(['VERCEL_ANALYTICS_ID']);
updateConfig({
outDir,
build: {
format: 'directory',
redirects: false,
},
vite: {
define: viteDefine,
...getSpeedInsightsViteConfig(speedInsights?.enabled || analytics),
},
...getAstroImageConfig(
imageService,
Expand Down
3 changes: 3 additions & 0 deletions packages/integrations/vercel/src/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import type { AnalyticsProps } from '@vercel/analytics';

export type VercelWebAnalyticsBeforeSend = AnalyticsProps['beforeSend'];
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel/serverless';

export default defineConfig({
adapter: vercel({
speedInsights: {
enabled: true
}
})
});
Loading
Loading