serialize-javascript
Serializes JavaScript values-including functions, RegExps, Dates, Maps, Sets, and BigInts-into a superset of JSON that's safe to embed directly in an HTML script tag.
Repository Health
Technical Analysis
serialize-javascript is a small, dependency-free npm library that serializes JavaScript values into a superset of JSON capable of representing functions, regular expressions, dates, Maps, Sets, sparse arrays, BigInts, and URL objects - types that JSON.stringify() either drops or mangles. The output is literal JavaScript that can be written to a .js file or embedded directly into an HTML document as the contents of a <script> element, which is the library’s primary use case: sharing server-computed state (including things like route regexps) with client-side code during server-rendered page loads.
Beyond format support, the library treats safe HTML embedding as a first-class concern rather than an afterthought. It automatically escapes HTML-unsafe characters and invalid JavaScript line terminators, and uses a dedicated string/comment/regex-literal scanner to correctly escape </script>-like sequences inside serialized function bodies without corrupting arrow-function syntax or regex literals. Recent versions add explicit validation of Date ISO strings, RegExp flags, and URL string output before splicing them back into the result, guarding against spoofed toJSON/toString overrides being used to inject code.
What You Get
- A single
serialize(value, options)function with zero runtime dependencies. - Automatic escaping of HTML-unsafe characters and JS line terminators so output is safe for inline
<script>embedding. - Support for serializing non-JSON types: functions, RegExp, Date, Map, Set, sparse Array, BigInt, and URL.
- Options (
space,isJSON,unsafe,ignoreFunction) to control formatting, performance, escaping behavior, and function handling.
Common Use Cases
- Bootstrapping client-side app state from server-rendered HTML.
- Safely embedding dynamic, server-computed data inside inline
<script>tags without XSS risk. - Serializing values JSON can’t represent (functions, RegExps, Dates, Maps, Sets, BigInts, URLs) for storage or transmission.
- Sharing route/regex definitions between server and client in isomorphic web apps.
Under The Hood
Architecture
The entire module is a single file (index.js, ~350 lines) with no internal layering: it’s one exported serialize() function built around a JSON.stringify replacer pattern. Values JSON can’t represent (functions, RegExp, Date, Map, Set, sparse Array, BigInt, URL) are diverted through a custom replacer into typed placeholder tokens (@__F-<uid>-<n>__@, @__R-<uid>-<n>__@, etc.), collected into per-type buffers, then spliced back into the JSON string via a single global regex pass keyed by placeholder type; a per-call random UID prevents placeholder collision or spoofing from attacker-controlled string content, and recursive container types (Map/Set/sparse-Array entries) call serialize() again internally. The placeholder token shape functions as a de facto internal protocol between the replacer and the final regex-replace step, so both directions of translation are tightly coupled to it.
Tech Stack
Zero runtime dependencies - the only devDependency is benchmark, used solely by the test/benchmark/serialize.js script. Plain CommonJS (module.exports), targeting engines.node >= 20.0.0, and relying only on built-in Node/Web APIs: JSON.stringify with a custom replacer, crypto.getRandomValues for UID generation, and standard globals Map/Set/URL/BigInt/RegExp. The test suite uses Node’s built-in node:test and node:assert rather than a third-party framework. CI runs the suite across a matrix of Node versions via GitHub Actions, with a separate tag-triggered workflow that runs tests and then publishes to npm using OIDC trusted publishing. No bundler, transpiler, or build step - the package is published as-is.
Code Quality
Tests are extensive for the module’s scope, covering undefined/null/JSON passthrough, functions, regexps, dates, Maps, Sets, BigInt, URL, XSS-escaping behavior, and option combinations, run directly via the native node --test runner with no test-framework configuration layer. There is no TypeScript and no shipped type definitions, so correctness relies entirely on runtime tests rather than compile-time checks. Error handling is explicit and security-motivated: serialization throws on native-code functions, and the placeholder-replacement step throws on malformed Date ISO strings or non-string RegExp.source/URL.toString() results, a deliberate hardening pattern with comments that call out protecting against spoofed toJSON/toString overrides. No linter or formatter configuration is present in the repository. CI exercises the full test matrix on every push and pull request.
What Makes It Unique
The library’s distinguishing choice is treating “safe to embed in a <script> tag” as a first-class security property of serialization rather than an afterthought: HTML-unsafe characters and JS line terminators are escaped automatically, and a purpose-built scanner distinguishes real code from string/template/regex-literal/comment spans so </script>-like sequences inside serialized function bodies are escaped correctly without breaking arrow-function syntax or regex literals. Its extended type support (functions, RegExp, Date, Map, Set, sparse Array, BigInt, URL) is not unique among serialization libraries, but the depth of its threat-model awareness - validating ISO date strings, sanitizing RegExp flags, and type-checking URL/RegExp string output before splicing placeholders back into the result to guard against spoofed accessor overrides - is unusually careful for a small, single-purpose utility.