From 3e2699f38f12accb56059bc46d755bc54e8ea5f4 Mon Sep 17 00:00:00 2001 From: Lennart Date: Sun, 7 Jan 2024 12:44:38 +0100 Subject: [PATCH 01/25] chore: Improve README --- README.md | 87 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 72 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 86a1017..4b290bc 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,18 @@ # svelte-plausible-analytics -Add Plausible Analytics to a SvelteKit app and track analytics and custom events. +Add Plausible Analytics to a SvelteKit app to track analytics and custom events. -*Important* - requires a [Plausible Analytics](https://plausible.io/) account. -All events require custom goals to be configured in the Plausible Analytics dashboard. +**Important:** `svelte-plausible-analytics` requires a [Plausible Analytics](https://plausible.io/) account or a [self-hosted](https://plausible.io/docs/self-hosting) Plausible instance. -## Install the package +## Installation -```bash +```shell npm i --save-dev @accuser/svelte-plausible-analytics ``` -## Examples +## Usage -Add Plausible Analytics to the root layout to track page views. +It is recommended to place your `` component to the root layout of your app. In most cases this will be `src/routes/+layout.svelte`. ```svelte + + + ``` +1. Use the `addEvent` method to track an event. The first argument is the event name (required), the second argument is optional and can be used to [attach custom properties](https://plausible.io/docs/custom-props/for-custom-events#2-using-the-manual-method). + ```svelte + + + + ``` + +For common events `@accuser/svelte-plausible-analytics` provides helper functions to enforce certain props. Feel free to only use `addEvent` if they don't fit your requirements. As an example, you can use the `login` method like so: ```svelte - + ``` -Track custom events: +Behind the scenes this helper function sends the event with the name `login` and a custom property of `method: 'CTA'`. You can find all helper functions on [GitHub](https://github.com/accuser/svelte-plausible-analytics/blob/main/src/lib/store.ts) or through IntelliSense in your IDE. + +**Important:** You need to create a custom event goal in your dashboard when using any of the event methods. + +## Examples + +Here are some further usage examples. If you have a snippet that you'd think might be helpful for others, please send them in through a pull request! + +### 404 page + +File: `src/routes/+error.svelte` ```svelte +``` - const { addEvent } = pa; +### Using a proxy + +You can [proxy your script](https://plausible.io/docs/proxy/introduction) to deal with adblockers. You'll want to set the `apiHost` prop to your domain. + +File: `src/routes/+layout.svelte` + +```svelte + - + ``` ## Contributors From 9edb4fca8fb6519e0c65b3f13edb318c53cfbade Mon Sep 17 00:00:00 2001 From: Lennart Date: Sun, 7 Jan 2024 12:54:34 +0100 Subject: [PATCH 02/25] chore: Typo fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b290bc..488d5a7 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ npm i --save-dev @accuser/svelte-plausible-analytics ## Usage -It is recommended to place your `` component to the root layout of your app. In most cases this will be `src/routes/+layout.svelte`. +It is recommended to add your `` component to the root layout of your app. In most cases this will be `src/routes/+layout.svelte`. ```svelte - - - - - {#if enabled} - - - {/if} - diff --git a/src/lib/components/PlausibleAnalytics.svelte b/src/lib/components/PlausibleAnalytics.svelte new file mode 100644 index 0000000..f48d1ff --- /dev/null +++ b/src/lib/components/PlausibleAnalytics.svelte @@ -0,0 +1,100 @@ + + + + {#if domain && enabled} + + + {/if} + diff --git a/src/lib/components/PlausibleAnalytics.test.ts b/src/lib/components/PlausibleAnalytics.test.ts new file mode 100644 index 0000000..f2af107 --- /dev/null +++ b/src/lib/components/PlausibleAnalytics.test.ts @@ -0,0 +1,196 @@ +import { JSDOM } from 'jsdom'; +import { mount } from 'svelte'; +import { beforeEach, describe, expect, it } from 'vitest'; +import PlausibleAnalytics from './PlausibleAnalytics.svelte'; + +describe('PlausibleAnalytics', () => { + beforeEach(async () => { + document = window.document = (await JSDOM.fromFile('src/app.html')).window.document; + }); + + describe('in development mode', () => { + beforeEach(() => { + import.meta.env.MODE = 'development'; + }); + + it('renders nothing with default props', async () => { + mount(PlausibleAnalytics, { target: document.body }); + + expect(document.querySelectorAll('script').length).toBe(0); + }); + + it('renders nothing when `enabled` is `false`', async () => { + mount(PlausibleAnalytics, { props: { enabled: false }, target: document.body }); + + expect(document.querySelectorAll('script').length).toBe(0); + }); + + it('renders scripts when `enabled` is `true`', async () => { + mount(PlausibleAnalytics, { props: { enabled: true }, target: document.body }); + + expect(document.querySelectorAll('script').length).toBe(2); + }); + }); + + describe('in production mode', () => { + beforeEach(() => { + import.meta.env.MODE = 'production'; + }); + + it('renders with default props', async () => { + mount(PlausibleAnalytics, { target: document.body }); + + expect(document.querySelectorAll('script').length).toBe(2); + }); + + it('renders nothing when `enabled` is `false`', async () => { + mount(PlausibleAnalytics, { props: { enabled: false }, target: document.body }); + + expect(document.querySelectorAll('script').length).toBe(0); + }); + + it('renders scripts when `enabled` is `true`', async () => { + mount(PlausibleAnalytics, { props: { enabled: true }, target: document.body }); + + expect(document.querySelectorAll('script').length).toBe(2); + }); + }); + + it('includes "compat" in `src` attribute when `compat` is `true`', async () => { + mount(PlausibleAnalytics, { props: { compat: true }, target: document.body }); + + expect(document.querySelector('script[data-api]')).toBeInTheDocument(); + expect(document.querySelector('script[data-api]')).toHaveAttribute('src'); + expect(document.querySelector('script[data-api]')?.getAttribute('src')).toContain('compat'); + }); + + for (const value of [false, undefined]) { + it(`does not include "compat" in \`src\` attribute when \`compat\` is \`${value}\``, async () => { + mount(PlausibleAnalytics, { props: { compat: value }, target: document.body }); + + expect(document.querySelector('script[data-api]')).toBeInTheDocument(); + expect(document.querySelector('script[data-api]')).toHaveAttribute('src'); + expect(document.querySelector('script[data-api]')?.getAttribute('src')).not.toContain( + 'compat' + ); + }); + } + + it('includes `${apiHost}/api/event` in `data-api` attribute when `apiHost` is set', async () => { + mount(PlausibleAnalytics, { props: { apiHost: 'example.com' }, target: document.body }); + + expect(document.querySelector('script[data-api]')).toBeInTheDocument(); + expect(document.querySelector('script[data-api]')?.getAttribute('data-api')).toContain( + 'example.com/api/event' + ); + }); + + it('includes `https://plausible.io/api/event` in `data-domain` attribute when `domain` is not set', async () => { + mount(PlausibleAnalytics, { target: document.body }); + + expect(document.querySelector('script[data-api]')).toBeInTheDocument(); + expect(document.querySelector('script[data-api]')?.getAttribute('data-api')).toContain( + 'https://plausible.io/api/event' + ); + }); + + it('includes `domain` in `data-domain` attribute when `domain` is set', async () => { + mount(PlausibleAnalytics, { props: { domain: 'example.com' }, target: document.body }); + + expect(document.querySelector('script[data-domain]')).toBeInTheDocument(); + expect(document.querySelector('script[data-domain]')?.getAttribute('data-domain')).toContain( + 'example.com' + ); + }); + + it('includes `localhost` in `data-domain` attribute when `domain` is not set', async () => { + mount(PlausibleAnalytics, { target: document.body }); + + expect(document.querySelector('script[data-domain]')).toBeInTheDocument(); + expect(document.querySelector('script[data-domain]')?.getAttribute('data-domain')).toContain( + 'localhost' + ); + }); + + it('includes "file-downloads" in `src` attribute when `fileDownloads` is `true`', async () => { + mount(PlausibleAnalytics, { props: { fileDownloads: true }, target: document.body }); + + expect(document.querySelector('script[data-api]')).toBeInTheDocument(); + expect(document.querySelector('script[data-api]')?.getAttribute('src')).toContain( + 'file-downloads' + ); + }); + + for (const value of [false, undefined]) { + it(`does not include "file-downloads" in \`src\` attribute when \`fileDownloads\` is \`${value}\``, async () => { + mount(PlausibleAnalytics, { props: { fileDownloads: value }, target: document.body }); + + expect(document.querySelector('script[data-api]')).toBeInTheDocument(); + expect(document.querySelector('script[data-api]')?.getAttribute('src')).not.toContain( + 'file-downloads' + ); + }); + } + + for (const prop of ['hash', 'hashMode']) { + it(`includes "hash" in \`src\` attribute when \`${prop}\` is \`true\``, async () => { + mount(PlausibleAnalytics, { props: { [prop]: true }, target: document.body }); + + expect(document.querySelector('script[data-api]')).toBeInTheDocument(); + expect(document.querySelector('script[data-api]')?.getAttribute('src')).toContain('hash'); + }); + + for (const value of [false, undefined]) { + it(`does not include "hash" in \`src\` attribute when \`${prop}\` is \`${value}\``, async () => { + mount(PlausibleAnalytics, { props: { [prop]: value }, target: document.body }); + + expect(document.querySelector('script[data-api]')).toBeInTheDocument(); + expect(document.querySelector('script[data-api]')?.getAttribute('src')).not.toContain( + 'hash' + ); + }); + } + } + + for (const prop of ['local', 'trackLocalhost']) { + it(`includes "local" in \`src\` attribute when \`${prop}\` is \`true\``, async () => { + mount(PlausibleAnalytics, { props: { [prop]: true }, target: document.body }); + + expect(document.querySelector('script[data-api]')).toBeInTheDocument(); + expect(document.querySelector('script[data-api]')?.getAttribute('src')).toContain('local'); + }); + + for (const value of [false, undefined]) { + it(`does not include "local" in \`src\` attribute when \`${prop}\` is \`${value}\``, async () => { + mount(PlausibleAnalytics, { props: { [prop]: value }, target: document.body }); + + expect(document.querySelector('script[data-api]')).toBeInTheDocument(); + expect(document.querySelector('script[data-api]')?.getAttribute('src')).not.toContain( + 'local' + ); + }); + } + } + + for (const prop of ['outboundLinks', 'trackOutboundLinks']) { + it(`includes "outbound-links" in \`src\` attribute when \`${prop}\` is \`true\``, async () => { + mount(PlausibleAnalytics, { props: { [prop]: true }, target: document.body }); + + expect(document.querySelector('script[data-api]')).toBeInTheDocument(); + expect(document.querySelector('script[data-api]')?.getAttribute('src')).toContain( + 'outbound-links' + ); + }); + + for (const value of [false, undefined]) { + it(`does not include "outbound-linkg" in \`src\` attribute when \`${prop}\` is \`${value}\``, async () => { + mount(PlausibleAnalytics, { props: { [prop]: value }, target: document.body }); + + expect(document.querySelector('script[data-api]')).toBeInTheDocument(); + expect(document.querySelector('script[data-api]')?.getAttribute('src')).not.toContain( + 'outbound-links' + ); + }); + } + } +}); From 1922ceccdb8c7e519a4e55439f63da606370306f Mon Sep 17 00:00:00 2001 From: Matthew Gibbons Date: Tue, 1 Oct 2024 06:30:01 +0000 Subject: [PATCH 14/25] Unit tests are better --- tests/test.ts | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 tests/test.ts diff --git a/tests/test.ts b/tests/test.ts deleted file mode 100644 index 5a53e8c..0000000 --- a/tests/test.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { expect, test } from '@playwright/test'; - -test('PlausibleAnalytics provider includes correct + + + +{@render children?.()} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 77cee9f..f76d8e9 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,5 +1 @@ - - - +

Hello, World!

From 4c49d0c1cd21772d63b5e5a42c1a5ba93eecf43f Mon Sep 17 00:00:00 2001 From: Matthew Gibbons Date: Tue, 1 Oct 2024 06:30:41 +0000 Subject: [PATCH 16/25] Export defaults --- src/lib/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/index.ts b/src/lib/index.ts index 290da8b..67afe38 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1,2 +1,2 @@ -export { default as PlausibleAnalytics } from './PlausibleAnalytics.svelte'; -export { pa } from './store.js'; +export { default as PlausibleAnalytics } from '$lib/components/PlausibleAnalytics.svelte'; +export { default as pa } from './pa.js'; From e43818d242c86ebebbbbada74653169950aafe95 Mon Sep 17 00:00:00 2001 From: Matthew Gibbons Date: Thu, 21 Dec 2023 16:52:38 +0000 Subject: [PATCH 17/25] Added contributors --- package.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/package.json b/package.json index 6e66ee3..66dbca2 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,16 @@ "name": "Matthew Gibbons", "url": "https://github.com/accuser" }, + "contributors": [ + { + "name": "Jeffrey Palmer", + "url": "https://github.com/JeffreyPalmer" + }, + { + "name": "Dan Grebb", + "url": "https://github.com/dgrebb" + } + ], "repository": { "type": "git", "url": "git+https://github.com/accuser/svelte-plausible-analytics.git" From 5304b782865bad073963fc6f2a883643afa4d412 Mon Sep 17 00:00:00 2001 From: Matthew Gibbons Date: Thu, 21 Dec 2023 17:00:43 +0000 Subject: [PATCH 18/25] Fixed homepage URL --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 66dbca2..4f02a63 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "svelte", "sveltekit" ], - "homepage": "https://github.com/accuser/svelte-plausible-analytics/README.md", + "homepage": "https://github.com/accuser/svelte-plausible-analytics", "bugs": { "url": "https://github.com/accuser/svelte-plausible-analytics/issues" }, From 80dd0295f447e3f2f1a45edec5c22be5a67c195b Mon Sep 17 00:00:00 2001 From: Matthew Gibbons Date: Tue, 1 Oct 2024 06:59:14 +0000 Subject: [PATCH 19/25] Added LekoArts to contributors --- package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/package.json b/package.json index 4f02a63..fcd43a0 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,10 @@ { "name": "Dan Grebb", "url": "https://github.com/dgrebb" + }, + { + "name": "Lennart", + "url": "https://github.com/LekoArts" } ], "repository": { From 3e52b9f31b6813f4318435fadfc1f5f8190c1346 Mon Sep 17 00:00:00 2001 From: Matthew Gibbons Date: Tue, 1 Oct 2024 08:29:08 +0000 Subject: [PATCH 20/25] Move to development dependencies --- package.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index fcd43a0..0deaa44 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "eslint-config-prettier": "^9.1.0", "eslint-plugin-svelte": "^2.44.1", "jsdom": "^25.0.1", + "plausible-tracker": "^0.3.9", "prettier": "^3.3.3", "prettier-plugin-svelte": "^3.2.7", "publint": "^0.2.11", @@ -88,8 +89,5 @@ }, "svelte": "./dist/index.js", "types": "./dist/index.d.ts", - "type": "module", - "dependencies": { - "plausible-tracker": "^0.3.9" - } + "type": "module" } \ No newline at end of file From 7c56ae6b4a104abfe6d66bfed05d6763ea4ff00b Mon Sep 17 00:00:00 2001 From: Matthew Gibbons Date: Tue, 1 Oct 2024 08:31:46 +0000 Subject: [PATCH 21/25] Support all extensions (expect manual) --- src/lib/components/PlausibleAnalytics.svelte | 80 +++++++++++++++++--- 1 file changed, 68 insertions(+), 12 deletions(-) diff --git a/src/lib/components/PlausibleAnalytics.svelte b/src/lib/components/PlausibleAnalytics.svelte index f48d1ff..e1a153d 100644 --- a/src/lib/components/PlausibleAnalytics.svelte +++ b/src/lib/components/PlausibleAnalytics.svelte @@ -1,5 +1,5 @@ - {#if domain && enabled} - - + {#if enabled} + {#if domain} + + {/if} + {#if trackErrorPages} + + {/if} {/if} From efd8359a630eac12e999cf631ec5709e3e4d81b2 Mon Sep 17 00:00:00 2001 From: Matthew Gibbons Date: Tue, 1 Oct 2024 08:32:47 +0000 Subject: [PATCH 22/25] Correct path --- src/lib/components/PlausibleAnalytics.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/PlausibleAnalytics.svelte b/src/lib/components/PlausibleAnalytics.svelte index e1a153d..2fce091 100644 --- a/src/lib/components/PlausibleAnalytics.svelte +++ b/src/lib/components/PlausibleAnalytics.svelte @@ -1,5 +1,5 @@