Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
f6d6582
refactor: rewrite in typescript (#608)
MatteoGabriele Mar 13, 2025
06bf047
Merge branch 'master' into feat/v3
MatteoGabriele Mar 14, 2025
a70998d
chore(package.json): add unpkg field
MatteoGabriele Mar 14, 2025
fa715ba
chore: add beta version
MatteoGabriele Mar 14, 2025
05b0571
feat(ecommerce): group all e-commerce related events (#614)
MatteoGabriele Mar 15, 2025
d8aa5e4
refactor: resource as object
MatteoGabriele Mar 15, 2025
67fdc1e
refactor: create new union type
MatteoGabriele Mar 15, 2025
71eeb1c
fix: gtag types not working
MatteoGabriele Mar 16, 2025
130673c
feat: add useCustomScript
MatteoGabriele Mar 16, 2025
92aaefd
refactor: types
MatteoGabriele Mar 16, 2025
5e9452a
feat: add optin optout
MatteoGabriele Mar 16, 2025
0be917b
feat: add opt in for multiple accounts
MatteoGabriele Mar 16, 2025
b3be8a0
chore: version bump
MatteoGabriele Mar 16, 2025
7fc632c
feat: add skip same path
MatteoGabriele Mar 16, 2025
4ce1670
chore: bump version
MatteoGabriele Mar 16, 2025
1efb5f3
refactor: template initial type
MatteoGabriele Mar 17, 2025
49c5acd
feat: add nonce attribute
MatteoGabriele Mar 17, 2025
b9650bc
refactor: resolve when server
MatteoGabriele Mar 17, 2025
08ff84e
refactor: rename defer and preconnect
MatteoGabriele Mar 17, 2025
be05689
feat: add hooks
MatteoGabriele Mar 17, 2025
2d9034c
chore: adjust comments
MatteoGabriele Mar 17, 2025
3b01892
refactor: use gtag types
MatteoGabriele Mar 17, 2025
aec2764
fix: no events emitted
MatteoGabriele Mar 18, 2025
48fc4c0
fix: app_name not registered
MatteoGabriele Mar 18, 2025
4a1482a
refactor: tests
MatteoGabriele Mar 18, 2025
434b311
feat: add consent and consent mode
MatteoGabriele Mar 19, 2025
2d0c27c
chore: export consent methods
MatteoGabriele Mar 19, 2025
d44d9ff
chore: bump version
MatteoGabriele Mar 19, 2025
3340ef3
Merge branch 'master' into feat/v3
MatteoGabriele Mar 19, 2025
730fb85
feat: check if has gtag already
MatteoGabriele Mar 19, 2025
4bac76b
refactor: gtag params
MatteoGabriele Mar 19, 2025
94be411
refactor: remove useCustomScript since it's handled already
MatteoGabriele Mar 19, 2025
cb0b1fc
Merge branch 'feat/v3' of https://github.com/MatteoGabriele/vue-gtag …
MatteoGabriele Mar 19, 2025
8418919
refactor: use function keyword
MatteoGabriele Mar 19, 2025
7686cc7
perf: avoid barrel file
MatteoGabriele Mar 20, 2025
dd36fe6
fix: utils export
MatteoGabriele Mar 21, 2025
679b192
chore: move types
MatteoGabriele Mar 21, 2025
2aef51e
refactor: move appName to the settings root
MatteoGabriele Mar 21, 2025
754aa3b
feat: exclude is also a function
MatteoGabriele Mar 21, 2025
a328a49
feat: turn off send_page_view on page tracker
MatteoGabriele Mar 21, 2025
f5b5b55
refactor: move usefullpath and userouterbasepath to pagetracker
MatteoGabriele Mar 21, 2025
664a047
chore: format
MatteoGabriele Mar 21, 2025
10af159
feat: add use-gtag-with-consent composable
MatteoGabriele Mar 23, 2025
1748746
fix: check for ssr
MatteoGabriele Mar 23, 2025
9fc41cf
refactor: skip same path by default
MatteoGabriele Mar 23, 2025
df3705d
feat: add global properties
MatteoGabriele Mar 23, 2025
6809274
chore: add vue-router external
MatteoGabriele Mar 23, 2025
6eceae3
refactor: types and function names
MatteoGabriele Mar 26, 2025
58834a3
refactor: scaffolding
MatteoGabriele Mar 26, 2025
9402964
refactor: scaffolding
MatteoGabriele Mar 26, 2025
3eebc6c
refactor: composable exports
MatteoGabriele Mar 26, 2025
012973a
test: make sure to check global properties
MatteoGabriele Mar 26, 2025
d9d9132
chore: audit
MatteoGabriele Mar 26, 2025
18f991f
refactor: delete window ga-disable property
MatteoGabriele Mar 26, 2025
aff7440
refactor: add aliases
MatteoGabriele Mar 26, 2025
ece6bbb
feat: add init mode
MatteoGabriele Mar 26, 2025
484f652
chore: bump
MatteoGabriele Mar 26, 2025
e725ec5
chore: scaffold core folder
MatteoGabriele Mar 27, 2025
838890c
refactor: move create-gtag inside core
MatteoGabriele Mar 28, 2025
b9dc75d
chore: update docs
MatteoGabriele Mar 28, 2025
d700b5d
chore: bump
MatteoGabriele Mar 28, 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
3 changes: 3 additions & 0 deletions .github/workflows/deployment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ jobs:

- name: Run linter
run: npm run lint

- name: Run type check
run: npm run typecheck

- name: Run tests
run: npm run test:once
Expand Down
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@
"editor.codeActionsOnSave": {
"quickfix.biome": "explicit",
"source.organizeImports.biome": "explicit"
}
},
"editor.tabSize": 2,
"editor.insertSpaces": true,
"editor.detectIndentation": false
}
5 changes: 3 additions & 2 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
"files": {
"ignoreUnknown": false,
"ignore": ["dist"],
"include": ["./src"]
"include": ["./src/**/*.ts", "./tsconfig.json", "./global.d.ts"]
},
"formatter": {
"enabled": true,
"indentStyle": "space"
"indentStyle": "space",
"indentWidth": 2
},
"organizeImports": {
"enabled": true
Expand Down
2,364 changes: 2,021 additions & 343 deletions package-lock.json

Large diffs are not rendered by default.

24 changes: 14 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "vue-gtag",
"description": "Global Site Tag (gtag.js) plugin for Vue",
"type": "module",
"version": "2.1.1",
"version": "3.0.0-beta.8",
"author": {
"name": "Matteo Gabriele",
"email": "m.gabriele.dev@gmail.com"
Expand All @@ -15,9 +15,11 @@
"scripts": {
"dev": "vite build --watch",
"fix": "biome check --write",
"build": "vite build",
"lint": "biome check",
"lint": "biome lint",
"build": "tsc --noEmit && vite build",
"typecheck": "tsc --noEmit",
"test": "vitest",
"test:cov": "vitest run --coverage",
"test:once": "vitest run",
"prepublishOnly": "npm run lint && npm run test:once && npm run build"
},
Expand All @@ -34,14 +36,12 @@
"vue",
"vuejs"
],
"main": "./dist/vue-gtag.cjs.js",
"module": "./dist/vue-gtag.esm.js",
"unpkg": "./dist/vue-gtag.umd.js",
"jsdelivr": "./dist/vue-gtag.umd.js",
"types": "vue-gtag.d.ts",
"main": "./dist/vue-gtag.js",
"module": "./dist/vue-gtag.js",
"unpkg": "./dist/vue-gtag.js",
"types": "./dist/vue-gtag.d.ts",
"files": [
"dist",
"vue-gtag.d.ts"
"dist"
],
"bugs": {
"url": "https://github.com/MatteoGabriele/vue-gtag/issues"
Expand All @@ -52,10 +52,14 @@
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@types/node": "^22.13.5",
"@vitest/coverage-v8": "^3.0.5",
"flush-promises": "^1.0.2",
"jsdom": "^26.0.0",
"mockdate": "^3.0.5",
"terser": "^5.39.0",
"vite": "^6.1.0",
"vite-plugin-dts": "^4.5.0",
"vitest": "^3.0.5",
"vue": "^3.0.0",
"vue-router": "^4.5.0"
Expand Down
19 changes: 0 additions & 19 deletions src/add-configuration.js

This file was deleted.

75 changes: 75 additions & 0 deletions src/add-configuration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import mockdate from "mockdate";
import addConfiguration from "./add-configuration";
import query from "./api/query";
import { resetSettings, updateSettings } from "./settings";

mockdate.set("2025-02-27");

vi.mock("./api/query");

describe("addConfiguration", () => {
beforeEach(resetSettings);

it("should query with initial configuration", () => {
updateSettings({
tagId: "UA-12345678",
});

addConfiguration();

expect(query).toHaveBeenNthCalledWith(1, "js", new Date());
expect(query).toHaveBeenNthCalledWith(2, "config", "UA-12345678", {
send_page_view: false,
});
});

it("should query multiple domains", () => {
updateSettings({
tagId: "UA-1",
additionalAccounts: [
{ tagId: "UA-2" },
{ tagId: "UA-3", config: { currency: "USD" } },
{ tagId: "UA-4", config: { send_page_view: true } },
],
});

addConfiguration();

expect(query).toHaveBeenNthCalledWith(1, "js", new Date());
expect(query).toHaveBeenNthCalledWith(2, "config", "UA-1", {
send_page_view: false,
});

expect(query).toHaveBeenNthCalledWith(3, "config", "UA-2", {
send_page_view: false,
groups: "default",
});

expect(query).toHaveBeenNthCalledWith(4, "config", "UA-3", {
currency: "USD",
send_page_view: false,
groups: "default",
});

expect(query).toHaveBeenNthCalledWith(5, "config", "UA-4", {
send_page_view: true,
groups: "default",
});
});

it("should add the linker", () => {
updateSettings({
tagId: "UA-1",
linker: {
domains: ["domain.com"],
},
});

addConfiguration();

expect(query).toHaveBeenNthCalledWith(1, "set", "linker", {
domains: ["domain.com"],
});
expect(query).toHaveBeenNthCalledWith(2, "js", new Date());
});
});
47 changes: 47 additions & 0 deletions src/add-configuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import linker from "./api/linker";
import query from "./api/query";
import { getSettings } from "./settings";
import type { ConfigParams } from "./types/settings";

function mergeDefaults(config: ConfigParams = {}): ConfigParams {
return {
send_page_view: false,
...config,
};
}

export default function addConfiguration() {
const {
tagId,
config,
groupName,
linker: linkerOptions,
additionalAccounts,
} = getSettings();

if (!tagId) {
return;
}

if (linkerOptions) {
linker(linkerOptions);
}

query("js", new Date());
query("config", tagId, mergeDefaults(config));

if (!additionalAccounts) {
return;
}

for (const account of additionalAccounts) {
query(
"config",
account.tagId,
mergeDefaults({
groups: groupName,
...account.config,
}),
);
}
}
147 changes: 147 additions & 0 deletions src/add-gtag.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { createRouter, createWebHistory } from "vue-router";
import addConfiguration from "./add-configuration";
import addGtag from "./add-gtag";
import addRouterTracking from "./add-router-tracking";
import { resetSettings, updateSettings } from "./settings";
import * as utils from "./utils";

vi.mock("./utils", async () => ({
...(await vi.importActual("./utils.ts")),
injectScript: vi.fn(),
}));

vi.mock("./add-configuration");
vi.mock("./add-router-tracking");
vi.mock("./api/query");

describe("addGtag", () => {
beforeEach(resetSettings);

it("should download the gtag.js library", async () => {
updateSettings({
tagId: "UA-12345678",
});

await addGtag();

const resource = "https://www.googletagmanager.com/gtag/js";
const preconnect = false;
const id = "UA-12345678";
const dataLayer = "dataLayer";
const url = `${resource}?id=${id}&l=${dataLayer}`;

expect(utils.injectScript).toHaveBeenCalledWith(url, {
preconnectOrigin: preconnect,
defer: false,
});
});

it("should download a custom version of the gtag.js library", async () => {
updateSettings({
tagId: "UA-12345678",
resource: {
url: "custom_resource_url",
},
});

await addGtag();

const resource = "custom_resource_url";
const id = "UA-12345678";
const dataLayer = "dataLayer";
const url = `${resource}?id=${id}&l=${dataLayer}`;

expect(utils.injectScript).toHaveBeenCalledWith(url, {
preconnectOrigin: false,
defer: false,
});
});

it("should fire callback when plugin is ready", async () => {
const spyOnReady = vi.fn();

updateSettings({
tagId: "UA-12345678",
onReady: spyOnReady,
});

await addGtag();

expect(spyOnReady).toHaveBeenCalled();
});

it("should fire callback when downloading library throws an error", async () => {
const spyOnError = vi.fn();

vi.spyOn(utils, "injectScript").mockRejectedValue(new Error());

updateSettings({
tagId: "UA-12345678",
onError: spyOnError,
});

await addGtag();

expect(spyOnError).toHaveBeenCalled();
});

it("should add initial gtag config call", async () => {
updateSettings({
tagId: "UA-12345678",
});

await addGtag();

expect(addConfiguration).toHaveBeenCalled();
});

it("should initialize router tracking", async () => {
const router = createRouter({
history: createWebHistory(),
routes: [{ path: "/", component: { template: "<div />" } }],
});

updateSettings({
tagId: "UA-12345678",
pageTracker: {
router,
},
});

await addGtag();

expect(addRouterTracking).toHaveBeenCalled();
expect(addConfiguration).toHaveBeenCalled();
});

it("should not bootstrap gtag if tagId is missing", async () => {
await addGtag();

expect(addConfiguration).not.toHaveBeenCalled();
});

it("should not inject the script in case the user has it already", async () => {
updateSettings({
tagId: "UA-12345678",
useCustomScript: true,
});

await addGtag();

expect(utils.injectScript).not.toHaveBeenCalled();
});

it("should fire the onReady callback even if using custom script loader", async () => {
const spyOnReady = vi.fn();

updateSettings({
tagId: "UA-12345678",
useCustomScript: true,
onReady: spyOnReady,
});

await addGtag();

expect(spyOnReady).toHaveBeenCalled();
});
});
Loading