Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions dist/module.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = function(...args) {
return import('./module.mjs').then(m => m.default.call(this, ...args))
}
const _meta = module.exports.meta = require('./module.json')
module.exports.getMeta = () => Promise.resolve(_meta)
43 changes: 43 additions & 0 deletions dist/module.d.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import * as _nuxt_schema from '@nuxt/schema';

declare const _default: _nuxt_schema.NuxtModule<Partial<{
enabled: boolean;
host: string;
id: string;
domains: string[] | null;
autoTrack: boolean;
ignoreLocalhost: boolean;
customEndpoint: string | null;
tag: string | null;
useDirective: boolean;
logErrors: boolean;
proxy: false | "direct" | "cloak";
trailingSlash: "any" | "always" | "never";
excludeQueryParams: boolean;
urlOptions?: Partial<{
trailingSlash: "any" | "always" | "never";
excludeSearch: boolean;
excludeHash: boolean;
}>;
}>, Partial<{
enabled: boolean;
host: string;
id: string;
domains: string[] | null;
autoTrack: boolean;
ignoreLocalhost: boolean;
customEndpoint: string | null;
tag: string | null;
useDirective: boolean;
logErrors: boolean;
proxy: false | "direct" | "cloak";
trailingSlash: "any" | "always" | "never";
excludeQueryParams: boolean;
urlOptions?: Partial<{
trailingSlash: "any" | "always" | "never";
excludeSearch: boolean;
excludeHash: boolean;
}>;
}>, false>;

export { _default as default };
43 changes: 43 additions & 0 deletions dist/module.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import * as _nuxt_schema from '@nuxt/schema';

declare const _default: _nuxt_schema.NuxtModule<Partial<{
enabled: boolean;
host: string;
id: string;
domains: string[] | null;
autoTrack: boolean;
ignoreLocalhost: boolean;
customEndpoint: string | null;
tag: string | null;
useDirective: boolean;
logErrors: boolean;
proxy: false | "direct" | "cloak";
trailingSlash: "any" | "always" | "never";
excludeQueryParams: boolean;
urlOptions?: Partial<{
trailingSlash: "any" | "always" | "never";
excludeSearch: boolean;
excludeHash: boolean;
}>;
}>, Partial<{
enabled: boolean;
host: string;
id: string;
domains: string[] | null;
autoTrack: boolean;
ignoreLocalhost: boolean;
customEndpoint: string | null;
tag: string | null;
useDirective: boolean;
logErrors: boolean;
proxy: false | "direct" | "cloak";
trailingSlash: "any" | "always" | "never";
excludeQueryParams: boolean;
urlOptions?: Partial<{
trailingSlash: "any" | "always" | "never";
excludeSearch: boolean;
excludeHash: boolean;
}>;
}>, false>;

export { _default as default };
12 changes: 12 additions & 0 deletions dist/module.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "nuxt-umami",
"version": "3.2.1",
"configKey": "umami",
"compatibility": {
"nuxt": ">=3"
},
"builder": {
"@nuxt/module-builder": "0.8.4",
"unbuild": "2.0.0"
}
}
222 changes: 222 additions & 0 deletions dist/module.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
import { defineNuxtModule, createResolver, addServerHandler, addTemplate, addImports, addPlugin } from '@nuxt/kit';
import { normalizeConfig, isValidString } from '../dist/runtime/utils.js';

const name = "nuxt-umami";
const version = "3.2.1";

const fn_faux = `const payload = load.payload;

if (enabled) {
if (!endpoint)
logger('endpoint', payload);
if (!website)
logger('id', payload);

return Promise.resolve({ ok: false });
}

logger('enabled');
return Promise.resolve({ ok: true });`;
const fn_proxy = `return ofetch('/api/savory', {
method: 'POST',
body: { ...load, cache },
})
.then(handleSuccess)
.catch(handleError);`;
const fn_direct = `const { type, payload } = load;

return ofetch(endpoint, {
method: 'POST',
headers: { ...(cache && { 'x-umami-cache': cache }) },
body: { type, payload: { ...payload, website } },
credentials: 'omit',
})
.then(handleSuccess)
.catch(handleError);`;
const collectFn = { fn_direct, fn_faux, fn_proxy };
function generateTemplate({
options: { mode, path, config: { logErrors, ...config } }
}) {
return `// template-generated
import { ofetch } from 'ofetch';
import { ${logErrors ? "logger" : "fauxLogger"} as $logger } from "${path.logger}";

/**
* @typedef {import("${path.types}").FetchFn} FetchFn
*
* @typedef {import("${path.types}").BuildPathUrlFn} BuildPathUrlFn
*
* @typedef {import("${path.types}").UmPublicConfig} UmPublicConfig
*/

export const logger = $logger;

/**
* @type UmPublicConfig
*/
export const config = ${JSON.stringify(config, null, 2)};

const { endpoint, website, enabled } = config;
let cache = '';

function handleError(err) {
try {
const cause = typeof err.data === 'string' ? err.data : err.data.data;
if (cause && typeof cause === 'string')
logger('collect', cause);
else throw new Error('Unknown error');
}
catch {
logger('collect', err);
}
return { ok: false };
}

function handleSuccess(response) {
cache = typeof response === 'string' ? response : '';
return { ok: true };
}

/**
* @type BuildPathUrlFn
*/
export function buildPathUrl(loc) {
try {
if (loc === null)
throw new Error('null value');

const url = new URL(loc, window.location.href);
const path = url.pathname;

${config.urlOptions.excludeHash && `url.hash = '';`}
${config.urlOptions.excludeSearch && `url.search = '';`}

url.pathname = ${config.urlOptions.trailingSlash === "always" ? `path.endsWith('/') ? path : path + '/'` : config.urlOptions.trailingSlash === "never" ? `path.endsWith('/') ? path.slice(0, -1) : path` : `path`};

return url.toString();
} catch {
return '';
}
}

/**
* @type FetchFn
*
* @variation ${mode}
*/
export async function collect(load) {
${collectFn[`fn_${mode}`]}
}
`;
}

const module = defineNuxtModule({
meta: {
name,
version,
configKey: "umami",
compatibility: {
nuxt: ">=3"
}
},
setup(options, nuxt) {
const { resolve } = createResolver(import.meta.url);
const pathTo = {
utils: resolve("./runtime/utils"),
logger: resolve("./runtime/logger"),
types: resolve("./types")
};
const runtimeConfig = nuxt.options.runtimeConfig;
const ENV = process.env;
const envHost = ENV.NUXT_UMAMI_HOST || ENV.NUXT_PUBLIC_UMAMI_HOST;
const envId = ENV.NUXT_UMAMI_ID || ENV.NUXT_PUBLIC_UMAMI_ID;
const envTag = ENV.NUXT_UMAMI_TAG || ENV.NUXT_PUBLIC_UMAMI_TAG;
const {
enabled,
host,
id,
customEndpoint,
domains,
proxy,
logErrors,
...runtimeOptions
} = normalizeConfig({
...options,
...isValidString(envId) && { id: envId },
...isValidString(envHost) && { host: envHost },
...isValidString(envTag) && { tag: envTag }
});
const endpoint = host ? new URL(host).origin + (customEndpoint || "/api/send") : "";
const publicConfig = {
...runtimeOptions,
enabled,
domains,
website: "",
endpoint: ""
};
const privateConfig = { endpoint: "", website: "", domains };
let mode = "faux";
const proxyOpts = ["direct", "cloak"];
if (enabled && endpoint && id) {
if (proxyOpts.includes(proxy)) {
if (proxy === "cloak") {
mode = "proxy";
addServerHandler({
route: "/api/savory",
handler: resolve("./runtime/server/endpoint")
});
privateConfig.endpoint = endpoint;
privateConfig.website = id;
} else if (proxy === "direct") {
mode = "direct";
publicConfig.endpoint = "/api/savory";
publicConfig.website = id;
nuxt.options.routeRules ||= {};
nuxt.options.routeRules["/api/savory"] = { proxy: endpoint };
}
} else {
mode = "direct";
publicConfig.endpoint = endpoint;
publicConfig.website = id;
}
} else {
if (!id)
console.warn("[umami] id is missing or incorrectly configured. Check module config.");
if (!endpoint) {
console.warn(
"[umami] Your API endpoint is missing or incorrectly configured. Check `host` and/or `customEndpoint` in module config."
);
}
console.info(`[umami] ${enabled ? "Currently running in test mode due to incorrect/missing options." : "Umami is disabled."}`);
}
runtimeConfig._proxyUmConfig = privateConfig;
addTemplate({
getContents: generateTemplate,
filename: "umami.config.mjs",
write: true,
options: {
mode,
config: {
...publicConfig,
logErrors: process.env.NODE_ENV === "development" || logErrors
},
path: pathTo
}
});
const composables = ["umTrackEvent", "umTrackView", "umIdentify", "umTrackRevenue"];
addImports(composables.map((name2) => {
return {
name: name2,
as: name2,
from: resolve("runtime/composables")
};
}));
addPlugin({
name: "nuxt-umami",
src: resolve("./runtime/plugin"),
mode: "all"
});
}
});

export { module as default };
49 changes: 49 additions & 0 deletions dist/runtime/composables.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { CurrencyCode, EventData, FetchResult } from '../types.js';
/**
* Track page views
*
* Both params are optional and will be automatically inferred
* @param path url being tracked, eg `/about`, `/contact?by=phone#office`
* @param referrer page referrer, `document.referrer`
*/
declare function umTrackView(path?: string, referrer?: string): FetchResult;
/**
* Tracks an event with a custom event type.
*
* @param eventName event name, eg 'CTA-button-click'
* @param eventData additional data for the event, provide an object in the format
* `{key: value}`, where `key` = `string`, `value` = `string | number | boolean`.
*/
declare function umTrackEvent(eventName: string, eventData?: EventData): FetchResult;
/**
* Save data about the current session with optional distinct user ID.
*
* Umami supports saving session data and identifying users with a distinct ID.
* @see [v2.13.0 release](https://github.com/umami-software/umami/releases/tag/v2.13.0)
* @see [v2.18.0 distinct IDs](https://github.com/umami-software/umami/releases/tag/v2.18.0)
*
* @param idOrData distinct user ID (string) or session data object
* @param sessionData optional session data when first param is a distinct ID
*
* @example
* // ID only
* umIdentify('user@example.com')
*
* // ID with data
* umIdentify('user@example.com', { name: 'John', plan: 'pro' })
*
* // Data only (backward compatible)
* umIdentify({ name: 'John', plan: 'pro' })
*/
declare function umIdentify(idOrData?: string | EventData, sessionData?: EventData): FetchResult;
/**
* Tracks financial performance
* @see [Umami Docs](https://umami.is/docs/reports/report-revenue)
*
* @param eventName [revenue] event name
* @param revenue revenue / amount
* @param currency currency code (defaults to USD)
* ([ISO 4217](https://en.wikipedia.org/wiki/ISO_4217#List_of_ISO_4217_currency_codes))
*/
declare function umTrackRevenue(eventName: string, revenue: number, currency?: CurrencyCode): FetchResult;
export { umIdentify, umTrackEvent, umTrackRevenue, umTrackView };
Loading