Skip to content
Merged
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: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ Three entry points:

All three ship dual ESM/CJS builds with TypeScript declarations. The `/react` and `/server` subpaths need TypeScript `moduleResolution` set to `node16` or `nodenext` (TS 4.7+), or `bundler` (TS 5.0+); under the legacy `node`/`node10` setting only the root entry resolves types, though all three resolve at runtime in Node.

The root entry is a barrel, so for a browser bundle it matters how you import from it. A static `import { toSnakeCase }` prunes to roughly 1.4KB on esbuild, and to well under 1KB on Rollup and webpack. A `require()` of the same entry keeps all of it on esbuild, Rollup and Rolldown — roughly 28KB minified — because none of them tree-shake CommonJS; webpack 5 does prune CommonJS exports, so there the gap is about 640 bytes. `await import()` is not a dependable substitute: esbuild and Rolldown keep the whole namespace however you access it, and Rollup prunes it only when you destructure the result directly. Use a static `import` at the call site. The subpaths are far less exposed, because most of what they contain is used either way: `/server` differs by a few hundred bytes, and `/react` by about 2.7KB on esbuild and about 500 bytes on webpack, against the ~14KB the hook pulls in regardless.

## Quick Start

### Basic Usage (Framework-Agnostic)
Expand Down Expand Up @@ -850,7 +852,7 @@ The browser defaults are lenient because losing a campaign label client-side is

`/server` does not import storage, form population, link decoration, debug helpers or React. That restriction is enforced by a test that walks the module's runtime import graph, not just documented — so the guarantee cannot be quietly removed by a convenient re-export.

Note the honest framing: the root entry does **not** crash in Node. The reasons to use `/server` are the documented DOM-free surface, the server-appropriate defaults, the totality contract, and a smaller install/parse surface — the server bundle is roughly 5KB against the root entry's 52KB.
Note the honest framing: the root entry does **not** crash in Node. The reasons to use `/server` are the documented DOM-free surface, the server-appropriate defaults, the totality contract, and a smaller install/parse surface — the published `/server` ESM build is about 6KB against the root entry's 52KB, the CommonJS files being slightly larger. Those are raw file sizes in the package rather than what a consumer ships: under a static `import` a bundler prunes both, and almost all of `/server` is reachable from its own exports anyway.

## Configuration Options

Expand Down Expand Up @@ -942,6 +944,7 @@ import type {
- The root entry is DOM-free at runtime — every `window` access sits behind a guard — so it imports and runs in Node without a DOM
- `@jackmisner/utm-toolkit/server` is the supported surface for server use: it cannot reach storage or the DOM, and that restriction is enforced by a test rather than only documented
- Types for the `/react` and `/server` subpaths need `moduleResolution` `node16`, `nodenext` or `bundler`; see [Installation](#installation)
- Unbundled Node loads the whole entry file whichever way you import it, so the bundle figures under [Installation](#installation) do not apply there: the cost is a one-off parse of about 52KB for the root entry, or about 6KB for `/server`

## Migration from Existing Projects

Expand Down
Loading