From cc2df1b095300fc93d8cec167305d5740d71c8f1 Mon Sep 17 00:00:00 2001 From: Vercel Date: Fri, 15 May 2026 09:36:10 +0000 Subject: [PATCH] Add Vercel Web Analytics integration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implemented Vercel Web Analytics for this project. ## What was implemented: This project is a **plain HTML static site** (not a framework like Next.js or React), so I followed the appropriate installation approach for vanilla JavaScript applications. ### Files Created: - `assets/analytics.js` - Copied the @vercel/analytics ES module (6.3KB) from the package to the assets folder ### Files Modified: - `index.html` - Added a module script to import and initialize Vercel Analytics using the `inject()` function - `package.json` - Added `@vercel/analytics` v2.0.1 as a dependency - `package-lock.json` - Updated to reflect the new dependency ## Implementation Details: The implementation follows the latest Vercel Analytics documentation (fetched from https://vercel.com/docs/analytics/quickstart on May 15, 2026). For plain HTML/vanilla JavaScript sites, the analytics are integrated by: 1. Installing the `@vercel/analytics` package (v2.0.1) 2. Copying the ES module to the assets directory for direct browser import 3. Adding an inline module script in the HTML `` that imports and calls `inject()` The `inject()` function automatically: - Detects the environment (production/development) - Injects the Vercel Analytics script tag with proper configuration - Enables automatic page view tracking - Handles script loading errors gracefully ## Testing: ✅ Verified the site serves correctly with the new analytics code ✅ Ran `npm run check` - all checks passed (except ruby which is not available in this environment) ✅ Validated JavaScript syntax for all modified files ✅ Confirmed analytics.js module exports are correctly imported ## Next Steps: Once deployed to Vercel: 1. Enable Web Analytics in the Vercel dashboard for this project 2. Deploy the changes 3. Verify analytics are working by checking the Network tab for requests to `/_vercel/insights/view` 4. Monitor analytics data in the Vercel dashboard ## Notes: The analytics will automatically work in production mode when deployed to Vercel. In development mode (local), it will use debug logging instead of sending data to the server. Co-authored-by: Vercel --- assets/analytics.js | 241 ++++++++++++++++++++++++++++++++++++++++++++ index.html | 4 + package-lock.json | 43 ++++++++ package.json | 1 + 4 files changed, 289 insertions(+) create mode 100644 assets/analytics.js diff --git a/assets/analytics.js b/assets/analytics.js new file mode 100644 index 0000000..a82a854 --- /dev/null +++ b/assets/analytics.js @@ -0,0 +1,241 @@ +// src/queue.ts +var initQueue = () => { + if (window.va) return; + window.va = function a(...params) { + if (!window.vaq) window.vaq = []; + window.vaq.push(params); + }; +}; + +// package.json +var name = "@vercel/analytics"; +var version = "2.0.1"; + +// src/utils.ts +function isBrowser() { + return typeof window !== "undefined"; +} +function detectEnvironment() { + try { + const env = process.env.NODE_ENV; + if (env === "development" || env === "test") { + return "development"; + } + } catch { + } + return "production"; +} +function setMode(mode = "auto") { + if (mode === "auto") { + window.vam = detectEnvironment(); + return; + } + window.vam = mode; +} +function getMode() { + const mode = isBrowser() ? window.vam : detectEnvironment(); + return mode || "production"; +} +function isProduction() { + return getMode() === "production"; +} +function isDevelopment() { + return getMode() === "development"; +} +function removeKey(key, { [key]: _, ...rest }) { + return rest; +} +function parseProperties(properties, options) { + if (!properties) return void 0; + let props = properties; + const errorProperties = []; + for (const [key, value] of Object.entries(properties)) { + if (typeof value === "object" && value !== null) { + if (options.strip) { + props = removeKey(key, props); + } else { + errorProperties.push(key); + } + } + } + if (errorProperties.length > 0 && !options.strip) { + throw Error( + `The following properties are not valid: ${errorProperties.join( + ", " + )}. Only strings, numbers, booleans, and null are allowed.` + ); + } + return props; +} +function computeRoute(pathname, pathParams) { + if (!pathname || !pathParams) { + return pathname; + } + let result = pathname; + try { + const entries = Object.entries(pathParams); + for (const [key, value] of entries) { + if (!Array.isArray(value)) { + const matcher = turnValueToRegExp(value); + if (matcher.test(result)) { + result = result.replace(matcher, `/[${key}]`); + } + } + } + for (const [key, value] of entries) { + if (Array.isArray(value)) { + const matcher = turnValueToRegExp(value.join("/")); + if (matcher.test(result)) { + result = result.replace(matcher, `/[...${key}]`); + } + } + } + return result; + } catch { + return pathname; + } +} +function turnValueToRegExp(value) { + return new RegExp(`/${escapeRegExp(value)}(?=[/?#]|$)`); +} +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} +function getScriptSrc(props) { + if (props.scriptSrc) { + return makeAbsolute(props.scriptSrc); + } + if (isDevelopment()) { + return "https://va.vercel-scripts.com/v1/script.debug.js"; + } + if (props.basePath) { + return makeAbsolute(`${props.basePath}/insights/script.js`); + } + return "/_vercel/insights/script.js"; +} +function loadProps(explicitProps, confString) { + var _a; + let props = explicitProps; + if (confString) { + try { + props = { + ...(_a = JSON.parse(confString)) == null ? void 0 : _a.analytics, + ...explicitProps + }; + } catch { + } + } + setMode(props.mode); + const dataset = { + sdkn: name + (props.framework ? `/${props.framework}` : ""), + sdkv: version + }; + if (props.disableAutoTrack) { + dataset.disableAutoTrack = "1"; + } + if (props.viewEndpoint) { + dataset.viewEndpoint = makeAbsolute(props.viewEndpoint); + } + if (props.eventEndpoint) { + dataset.eventEndpoint = makeAbsolute(props.eventEndpoint); + } + if (props.sessionEndpoint) { + dataset.sessionEndpoint = makeAbsolute(props.sessionEndpoint); + } + if (isDevelopment() && props.debug === false) { + dataset.debug = "false"; + } + if (props.dsn) { + dataset.dsn = props.dsn; + } + if (props.endpoint) { + dataset.endpoint = props.endpoint; + } else if (props.basePath) { + dataset.endpoint = makeAbsolute(`${props.basePath}/insights`); + } + return { + beforeSend: props.beforeSend, + src: getScriptSrc(props), + dataset + }; +} +function makeAbsolute(url) { + return url.startsWith("http://") || url.startsWith("https://") || url.startsWith("/") ? url : `/${url}`; +} + +// src/generic.ts +function inject(props = { + debug: true +}, confString) { + var _a; + if (!isBrowser()) return; + const { beforeSend, src, dataset } = loadProps(props, confString); + initQueue(); + if (beforeSend) { + (_a = window.va) == null ? void 0 : _a.call(window, "beforeSend", beforeSend); + } + if (document.head.querySelector(`script[src*="${src}"]`)) return; + const script = document.createElement("script"); + script.src = src; + for (const [key, value] of Object.entries(dataset)) { + script.dataset[key] = value; + } + script.defer = true; + script.onerror = () => { + const errorMessage = isDevelopment() ? "Please check if any ad blockers are enabled and try again." : "Be sure to enable Web Analytics for your project and deploy again. See https://vercel.com/docs/analytics/quickstart for more information."; + console.log( + `[Vercel Web Analytics] Failed to load script from ${src}. ${errorMessage}` + ); + }; + document.head.appendChild(script); +} +function track(name2, properties, options) { + var _a, _b; + if (!isBrowser()) { + const msg = "[Vercel Web Analytics] Please import `track` from `@vercel/analytics/server` when using this function in a server environment"; + if (isProduction()) { + console.warn(msg); + } else { + throw new Error(msg); + } + return; + } + if (!properties) { + (_a = window.va) == null ? void 0 : _a.call(window, "event", { name: name2, options }); + return; + } + try { + const props = parseProperties(properties, { + strip: isProduction() + }); + (_b = window.va) == null ? void 0 : _b.call(window, "event", { + name: name2, + data: props, + options + }); + } catch (err) { + if (err instanceof Error && isDevelopment()) { + console.error(err); + } + } +} +function pageview({ + route, + path +}) { + var _a; + (_a = window.va) == null ? void 0 : _a.call(window, "pageview", { route, path }); +} +var generic_default = { + inject, + track, + computeRoute +}; +export { + computeRoute, + generic_default as default, + inject, + pageview, + track +}; +//# sourceMappingURL=index.mjs.map \ No newline at end of file diff --git a/index.html b/index.html index 5244b83..ecd818c 100644 --- a/index.html +++ b/index.html @@ -18,6 +18,10 @@ +