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

feat: New @wxt-dev/analytics package #790

Open
wants to merge 50 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
5217279
feat: Analytics module
aklinker1 Jul 2, 2024
f99c1aa
Implement client and google analytics
aklinker1 Jul 3, 2024
328acbb
Cleanup
aklinker1 Jul 3, 2024
8ace11f
Add missing file
aklinker1 Jul 3, 2024
eba8788
Umami provider and cleanup
aklinker1 Jul 4, 2024
06fd7e4
Update package.json
aklinker1 Jul 9, 2024
107803b
Update package.json
aklinker1 Jul 9, 2024
563bd4e
Fix __dirname reference
aklinker1 Jul 9, 2024
e0d0618
Fix missing useAppConfig
aklinker1 Jul 9, 2024
d0623fe
Fix missing useAppConfig
aklinker1 Jul 9, 2024
5216d75
Fix useAppConfig fr?
aklinker1 Jul 9, 2024
ee53bb0
disable auto-imports
aklinker1 Jul 9, 2024
50d2170
Fix storage
aklinker1 Jul 9, 2024
55113d6
Remove wxt/storage
aklinker1 Jul 9, 2024
c69e99d
Remove wxt/storage
aklinker1 Jul 9, 2024
e4886af
docs: Update `wxt init` GIF
aklinker1 Sep 18, 2024
d96840b
WIP
aklinker1 Sep 22, 2024
c73a4b6
Merge remote-tracking branch 'origin/main' into analytics-module
aklinker1 Oct 22, 2024
15adc77
Refactor to generated module instead of plugin
aklinker1 Oct 22, 2024
e8e00e4
Fix build
aklinker1 Oct 22, 2024
f75c5ca
fix api check
aklinker1 Oct 22, 2024
a861dc1
fix code-generation
aklinker1 Oct 22, 2024
7879fab
Cleanup
aklinker1 Oct 22, 2024
93e6dd6
Improve debug logs
aklinker1 Oct 22, 2024
e98f29f
Fix page upload params
aklinker1 Oct 22, 2024
26cbef5
Properly track title
aklinker1 Oct 22, 2024
0416ff7
Cleanup metadata handling
aklinker1 Oct 22, 2024
3786c7b
Fix types
aklinker1 Oct 22, 2024
c431daf
Fix readme and required minimum version
aklinker1 Oct 31, 2024
90c2b7f
Bump version
aklinker1 Oct 31, 2024
446fd90
Merge remote-tracking branch 'origin/main' into analytics-module
aklinker1 Dec 29, 2024
b727453
docs: Update `wxt init` GIF
aklinker1 Dec 29, 2024
6fb1463
defineAnalyticsProvider and updated umami config
aklinker1 Dec 30, 2024
9536720
cleanup
aklinker1 Dec 30, 2024
3b9f395
Update README
aklinker1 Dec 30, 2024
baebe28
cleanup
aklinker1 Dec 30, 2024
be74af4
Add plugin
aklinker1 Dec 30, 2024
b34754b
Cleanup
aklinker1 Dec 30, 2024
fdee7c6
Cleanup
aklinker1 Dec 30, 2024
f218c30
Add background if missing
aklinker1 Dec 30, 2024
9ad04d3
Add missing docs
aklinker1 Dec 30, 2024
829525f
Update workflows
aklinker1 Dec 30, 2024
95369ff
fix prepare script
aklinker1 Dec 30, 2024
fe8e2ed
Fix build script
aklinker1 Dec 30, 2024
2faba91
chore(release): analytics-v0.4.0
aklinker1 Dec 30, 2024
c2090d6
sync deps
aklinker1 Dec 30, 2024
94027fa
Update docs
aklinker1 Jan 5, 2025
00352c2
Cleanup
aklinker1 Jan 5, 2025
bcd906e
add dependency optimizations automatically
aklinker1 Jan 7, 2025
1509809
chore(release): analytics-v0.4.1
aklinker1 Jan 7, 2025
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
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
default: wxt
type: choice
options:
- analytics
- auto-icons
- i18n
- module-react
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/sync-releases.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
default: wxt
type: choice
options:
- analytics
- auto-icons
- i18n
- module-react
Expand Down
21 changes: 15 additions & 6 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { version as i18nVersion } from '../../packages/i18n/package.json';
import { version as autoIconsVersion } from '../../packages/auto-icons/package.json';
import { version as unocssVersion } from '../../packages/unocss/package.json';
import { version as storageVersion } from '../../packages/storage/package.json';
import { version as analyticsVersion } from '../../packages/analytics/package.json';

const title = 'Next-gen Web Extension Framework';
const titleSuffix = ' – WXT';
Expand All @@ -23,6 +24,14 @@ const ogTitle = `${title}${titleSuffix}`;
const ogUrl = 'https://wxt.dev';
const ogImage = 'https://wxt.dev/social-preview.png';

const otherPackages = {
analytics: analyticsVersion,
'auto-icons': autoIconsVersion,
i18n: i18nVersion,
storage: storageVersion,
unocss: unocssVersion,
};

// https://vitepress.dev/reference/site-config
export default defineConfig({
titleTemplate: `:title${titleSuffix}`,
Expand Down Expand Up @@ -97,12 +106,12 @@ export default defineConfig({
'https://github.com/wxt-dev/wxt/blob/main/packages/wxt/CHANGELOG.md',
),
]),
navItem('Other Packages', [
navItem(`@wxt-dev/storage — ${storageVersion}`, '/storage'),
navItem(`@wxt-dev/auto-icons — ${autoIconsVersion}`, '/auto-icons'),
navItem(`@wxt-dev/i18n — ${i18nVersion}`, '/i18n'),
navItem(`@wxt-dev/unocss — ${unocssVersion}`, '/unocss'),
]),
navItem(
'Other Packages',
Object.entries(otherPackages).map(([name, version]) =>
navItem(`@wxt-dev/${name} — ${version}`, `/${name}`),
),
),
]),
],

Expand Down
1 change: 1 addition & 0 deletions docs/analytics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!--@include: ../packages/analytics/README.md-->
Binary file modified docs/assets/init-demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
269 changes: 269 additions & 0 deletions packages/analytics/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
# WXT Analytics

Report analytics events from your web extension extension.

## Supported Analytics Providers

- [Google Analytics 4 (Measurement Protocol)](#google-analytics-4-measurement-protocol)
- [Umami](#umami)

## Install With WXT

1. Install the NPM package:
```bash
pnpm i @wxt-dev/analytics
```
2. In your `wxt.config.ts`, add the WXT module:
```ts
export default defineConfig({
modules: ['@wxt-dev/analytics/module'],
});
```
3. In your `<srcDir>/app.config.ts`, add a provider:

```ts
// <srcDir>/app.config.ts
import { umami } from '@wxt-dev/analytics/providers/umami';

export default defineAppConfig({
analytics: {
debug: true,
providers: [
// ...
],
},
});
```

4. Then use the `#analytics` module to report events:

```ts
import { analytics } from '#analytics';

await analytics.track('some-event');
await analytics.page();
await analytics.identify('some-user-id');
analytics.autoTrack(document.body);
```

## Install Without WXT

1. Install the NPM package:
```bash
pnpm i @wxt-dev/analytics
```
2. Create an `analytics` instance:

```ts
// utils/analytics.ts
import { createAnalytics } from '@wxt-dev/analytics';

export const analytics = createAnalytics({
providers: [
// ...
],
});
```

3. Import your analytics module in the background to initialize the message listener:
```ts
// background.ts
import './utils/analytics';
```
4. Then use your `analytics` instance to report events:

```ts
import { analytics } from './utils/analytics';

await analytics.track('some-event');
await analytics.page();
await analytics.identify('some-user-id');
analytics.autoTrack(document.body);
```

## Providers

### Google Analytics 4 (Measurement Protocol)

The [Measurement Protocol](https://developers.google.com/analytics/devguides/collection/protocol/ga4) is an alternative to GTag for reporting events to Google Analytics for MV3 extensions.

> [Why use the Measurement Protocol instead of GTag?](https://developer.chrome.com/docs/extensions/how-to/integrate/google-analytics-4#measurement-protocol)

Follow [Google's documentation](https://developer.chrome.com/docs/extensions/how-to/integrate/google-analytics-4#setup-credentials) to obtain your credentials and put them in your `.env` file:

```dotenv
WXT_GA_API_SECRET='...'
```

Then add the `googleAnalytics4` provider to your `<srcDir>/app.config.ts` file:

```ts
import { googleAnalytics4 } from '@wxt-dev/analytics/providers/google-analytics-4';

export default defineAppConfig({
analytics: {
providers: [
googleAnalytics4({
apiSecret: import.meta.env.WXT_GA_API_SECRET,
measurementId: '...',
}),
],
},
});
```

### Umami

[Umami](https://umami.is/) is a privacy-first, open source analytics platform.

In Umami's dashboard, create a new website. The website's name and domain can be anything. Obviously, an extension doesn't have a domain, so make one up if you don't have one.

After the website has been created, save the website ID and domain to your `.env` file:

```dotenv
WXT_UMAMI_WEBSITE_ID='...'
WXT_UMAMI_DOMAIN='...'
```

Then add the `umami` provider to your `<srcDir>/app.config.ts` file:

```ts
import { umami } from '@wxt-dev/analytics/providers/umami';

export default defineAppConfig({
analytics: {
providers: [
umami({
apiUrl: 'https://<your-umami-instance>/api',
websiteId: import.meta.env.WXT_UMAMI_WEBSITE_ID,
domain: import.meta.env.WXT_UMAMI_DOMAIN,
}),
],
},
});
```

### Custom Provider

If your analytics platform is not supported, you can provide an implementation of the `AnalyticsProvider` type in your `app.config.ts` instead:

```ts
import { defineAnalyticsProvider } from '@wxt-dev/analytics/client';

interface CustomAnalyticsOptions {
// ...
}

const customAnalytics = defineAnalyticsProvider<CustomAnalyticsOptions>(
(analytics, analyticsConfig, providerOptions) => {
// ...
},
);

export default defineAppConfig({
analytics: {
providers: [
customAnalytics({
// ...
}),
],
},
});
```

Example `AnalyticsProvider` implementations can be found at [`./modules/analytics/providers`](https://github.com/wxt-dev/wxt/tree/main/packages/analytics/modules/analytics/providers).

## User Properties

User ID and properties are stored in `browser.storage.local`. To change this or customize where these values are stored, use the `userId` and `userProperties` config:

```ts
// app.config.ts
import { storage } from 'wxt/storage';

export default defineAppConfig({
analytics: {
userId: storage.defineItem('local:custom-user-id-key'),
userProperties: storage.defineItem('local:custom-user-properties-key'),
},
});
```

To set the values at runtime, use the `identify` function:

```ts
await analytics.identify(userId, userProperties);
```

Alternatively, a common pattern is to use a random string as the user ID. This keeps the actual user information private, while still providing useful metrics in your analytics platform. This can be done very easily using WXT's storage API:

```ts
// app.config.ts
import { storage } from 'wxt/storage';

export default defineAppConfig({
analytics: {
userId: storage.defineItem('local:custom-user-id-key', {
init: () => crypto.randomUUID(),
}),
},
});
```

If you aren't using `wxt` or `@wxt-dev/storage`, you can define custom implementations for the `userId` and `userProperties` config:

```ts
const analytics = createAnalytics({
userId: {
getValue: () => ...,
setValue: (userId) => ...,
}
})
```

## Auto-track UI events

Call `analytics.autoTrack(container)` to automatically track UI events so you don't have to manually add them. Currently it:

- Tracks clicks to elements inside the `container`

In your extension's HTML pages, you'll want to call it with `document`:

```ts
analytics.autoTrack(document);
```

But in content scripts, you usually only care about interactions with your own UI:

```ts
const ui = createIntegratedUi({
// ...
onMount(container) {
analytics.autoTrack(container);
},
});
ui.mount();
```

## Enabling/Disabling

By default, **analytics is disabled**. You can configure how the value is stored (and change the default value) via the `enabled` config:

```ts
// app.config.ts
import { storage } from 'wxt/storage';

export default defineAppConfig({
analytics: {
enabled: storage.defineItem('local:analytics-enabled', {
fallback: true,
}),
},
});
```

At runtime, you can call `setEnabled` to change the value:

```ts
analytics.setEnabled(true);
```
20 changes: 20 additions & 0 deletions packages/analytics/app.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { defineAppConfig } from 'wxt/sandbox';
import { googleAnalytics4 } from './modules/analytics/providers/google-analytics-4';
import { umami } from './modules/analytics/providers/umami';

export default defineAppConfig({
analytics: {
debug: true,
providers: [
googleAnalytics4({
apiSecret: '...',
measurementId: '...',
}),
umami({
apiUrl: 'https://umami.aklinker1.io/api',
domain: 'analytics.wxt.dev',
websiteId: '8f1c2aa4-fad3-406e-a5b2-33e8d4501716',
}),
],
},
});
21 changes: 21 additions & 0 deletions packages/analytics/build.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { defineBuildConfig } from 'unbuild';
import { resolve } from 'node:path';

// Build module and plugins
export default defineBuildConfig({
rootDir: 'modules/analytics',
outDir: resolve(__dirname, 'dist'),
entries: [
{ input: 'index.ts', name: 'module' },
{ input: 'client.ts', name: 'index' },
'background-plugin.ts',
'types.ts',
'providers/google-analytics-4.ts',
'providers/umami.ts',
],
externals: ['#analytics'],
replace: {
'import.meta.env.NPM': 'true',
},
declaration: true,
});
17 changes: 17 additions & 0 deletions packages/analytics/entrypoints/popup/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Popup</title>
</head>
<body>
<label>
<input id="enabledCheckbox" type="checkbox" />
&emsp;Analytics enabled
</label>
<button id="button1">Button 1</button>
<button class="cool-button">Button 2</button>
<script type="module" src="./main.ts"></script>
</body>
</html>
Loading