@snowplow/browser-tracker-core depends on the 11y old sha1 npm package (v1.1.1) and that sha1 package uses Buffer.isBuffer() internally to type-check its input:
// node_modules/sha1/sha1.js
else if (typeof Buffer !== 'undefined' && typeof Buffer.isBuffer == 'function' && Buffer.isBuffer(message))
Snowplow only ever passes a string to sha1 (cookie domain + path for the domain hash), so this Buffer code path is never reached at runtime. But compilers (e.g. webpack) doesn't know that at build time
Next.js ships a ProvidePlugin that auto-injects Buffer from next/dist/compiled/buffer/index.js whenever it sees a bare Buffer reference in any module. Since sha1 references Buffer, the entire ~28 KB Buffer polyfill gets bundled into every client-side page even though it's dead code
This affects the global bundle (_app chunk), so every single page pays the cost of all next.js apps which use snowplow
Also the published ESM dist (dist/index.module.js) does import hash from 'sha1', but sha1 itself only ships CommonJS (module.exports = api)..
That's why snowplow forces all bundlers to activate their CJS interop mode which can also interfere with tree-shaking depending on the bundler
@snowplow/browser-tracker-core (ESM)
→ sha1 (CJS, references Buffer)
→ next/dist/compiled/buffer/index.js (+28 KB polyfill)
The only usage is in libraries/browser-tracker-core/src/tracker/index.ts:
domainHash = hash((configCookieDomain || domainAlias) + (configCookiePath || '/')).slice(0, 4);
Impact
- +28 KB (raw) / ~7 KB (gzipped) added to every page's initial load
- Affects all Next.js / webpack 5 users of
@snowplow/browser-tracker
- The
sha1 package hasn't been updated in years and also depends on charenc and crypt
- Workaround: manually strip
Buffer from webpack's ProvidePlugin and resolve.fallback in next.config — brittle and easy to regress
Environment
@snowplow/browser-tracker-core 4.6.9
- Next.js 16.1.7 (webpack 5)
@snowplow/browser-tracker-coredepends on the 11y oldsha1npm package (v1.1.1) and thatsha1package usesBuffer.isBuffer()internally to type-check its input:Snowplow only ever passes a string to
sha1(cookie domain + path for the domain hash), so thisBuffercode path is never reached at runtime. But compilers (e.g. webpack) doesn't know that at build timeNext.js ships a
ProvidePluginthat auto-injectsBufferfromnext/dist/compiled/buffer/index.jswhenever it sees a bareBufferreference in any module. Sincesha1referencesBuffer, the entire ~28 KB Buffer polyfill gets bundled into every client-side page even though it's dead codeThis affects the global bundle (
_appchunk), so every single page pays the cost of all next.js apps which use snowplowAlso the published ESM dist (
dist/index.module.js) doesimport hash from 'sha1', butsha1itself only ships CommonJS (module.exports = api)..That's why snowplow forces all bundlers to activate their CJS interop mode which can also interfere with tree-shaking depending on the bundler
The only usage is in
libraries/browser-tracker-core/src/tracker/index.ts:Impact
@snowplow/browser-trackersha1package hasn't been updated in years and also depends oncharencandcryptBufferfrom webpack'sProvidePluginandresolve.fallbackin next.config — brittle and easy to regressEnvironment
@snowplow/browser-tracker-core4.6.9