diff --git a/README.md b/README.md index 9ffdf30..4e9d50b 100644 --- a/README.md +++ b/README.md @@ -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) @@ -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 @@ -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