| Serialize JavaScript | |
| ==================== | |
| Serialize JavaScript to a _superset_ of JSON that includes regular expressions, dates and functions. | |
| [![npm Version][npm-badge]][npm] | |
|  | |
| ## Overview | |
| The code in this package began its life as an internal module to [express-state][]. To expand its usefulness, it now lives as `serialize-javascript` — an independent package on npm. | |
| You're probably wondering: **What about `JSON.stringify()`!?** We've found that sometimes we need to serialize JavaScript **functions**, **regexps**, **dates**, **sets** or **maps**. A great example is a web app that uses client-side URL routing where the route definitions are regexps that need to be shared from the server to the client. | |
| The string returned from this package's single export function is literal JavaScript which can be saved to a `.js` file, or be embedded into an HTML document by making the content of a `<script>` element. | |
| > **HTML characters and JavaScript line terminators are escaped automatically.** | |
| Please note that serialization for ES6 Sets & Maps requires support for `Array.from` (not available in IE or Node < 0.12), or an `Array.from` polyfill. | |
| > [!WARNING] | |
| > It may be tempting to use this package as a way to pass arbitrary functions into [worker threads][], since you cannot pass them directly via `postMessage()`. However, passing functions between worker threads is not possible in the general case. This package lets you serialize *some* functions, but it has limitations. | |
| > | |
| > For instance, if a function references something from outside the function body, it will not run properly if serialized and deserialized. This could include [closed-over variables][] or imports from other packages. For a serialized function to run properly, it must be entirely self-contained. | |
| > | |
| > In general, it is not possible to send arbitrary JavaScript to a worker thread, and pretend it's running the same way it would run on the main thread. This package doesn't let you do that. | |
| ## Installation | |
| Install using npm: | |
| ```shell | |
| $ npm install serialize-javascript | |
| ``` | |
| ## Usage | |
| ```js | |
| var serialize = require('serialize-javascript'); | |
| serialize({ | |
| str : 'string', | |
| num : 0, | |
| obj : {foo: 'foo'}, | |
| arr : [1, 2, 3], | |
| bool : true, | |
| nil : null, | |
| undef: undefined, | |
| inf : Infinity, | |
| date : new Date("Thu, 28 Apr 2016 22:02:17 GMT"), | |
| map : new Map([['hello', 'world']]), | |
| set : new Set([123, 456]), | |
| fn : function echo(arg) { return arg; }, | |
| re : /([^\s]+)/g, | |
| big : BigInt(10), | |
| url : new URL('https://example.com/'), | |
| }); | |
| ``` | |
| The above will produce the following string output: | |
| ```js | |
| '{"str":"string","num":0,"obj":{"foo":"foo"},"arr":[1,2,3],"bool":true,"nil":null,"undef":undefined,"inf":Infinity,"date":new Date("2016-04-28T22:02:17.000Z"),"map":new Map([["hello","world"]]),"set":new Set([123,456]),"fn":function echo(arg) { return arg; },"re":new RegExp("([^\\\\s]+)", "g"),"big":BigInt("10"),"url":new URL("https://example.com/")}' | |
| ``` | |
| Note: to produce a beautified string, you can pass an optional second argument to `serialize()` to define the number of spaces to be used for the indentation. | |
| ### Automatic Escaping of HTML Characters | |
| A primary feature of this package is to serialize code to a string of literal JavaScript which can be embedded in an HTML document by adding it as the contents of the `<script>` element. In order to make this safe, HTML characters and JavaScript line terminators are escaped automatically. | |
| ```js | |
| serialize({ | |
| haxorXSS: '</script>' | |
| }); | |
| ``` | |
| The above will produce the following string, HTML-escaped output which is safe to put into an HTML document as it will not cause the inline script element to terminate: | |
| ```js | |
| '{"haxorXSS":"\\u003C\\u002Fscript\\u003E"}' | |
| ``` | |
| > You can pass an optional `unsafe` argument to `serialize()` for straight serialization. | |
| ### Options | |
| The `serialize()` function accepts an `options` object as its second argument. All options are being defaulted to `undefined`: | |
| #### `options.space` | |
| This option is the same as the `space` argument that can be passed to [`JSON.stringify`][JSON.stringify]. It can be used to add whitespace and indentation to the serialized output to make it more readable. | |
| ```js | |
| serialize(obj, {space: 2}); | |
| ``` | |
| #### `options.isJSON` | |
| This option is a signal to `serialize()` that the object being serialized does not contain any function or regexps values. This enables a hot-path that allows serialization to be over 3x faster. If you're serializing a lot of data, and know its pure JSON, then you can enable this option for a speed-up. | |
| **Note:** That when using this option, the output will still be escaped to protect against XSS. | |
| ```js | |
| serialize(obj, {isJSON: true}); | |
| ``` | |
| #### `options.unsafe` | |
| This option is to signal `serialize()` that we want to do a straight conversion, without the XSS protection. This options needs to be explicitly set to `true`. HTML characters and JavaScript line terminators will not be escaped. You will have to roll your own. | |
| ```js | |
| serialize(obj, {unsafe: true}); | |
| ``` | |
| #### `options.ignoreFunction` | |
| This option is to signal `serialize()` that we do not want serialize JavaScript function. | |
| Just treat function like `JSON.stringify` do, but other features will work as expected. | |
| ```js | |
| serialize(obj, {ignoreFunction: true}); | |
| ``` | |
| ## Deserializing | |
| For some use cases you might also need to deserialize the string. This is explicitly not part of this module. However, you can easily write it yourself: | |
| ```js | |
| function deserialize(serializedJavascript){ | |
| return eval('(' + serializedJavascript + ')'); | |
| } | |
| ``` | |
| **Note:** Don't forget the parentheses around the serialized javascript, as the opening bracket `{` will be considered to be the start of a body. | |
| ## License | |
| This software is free to use under the Yahoo! Inc. BSD license. | |
| See the [LICENSE file][LICENSE] for license text and copyright information. | |
| [npm]: https://www.npmjs.org/package/serialize-javascript | |
| [npm-badge]: https://img.shields.io/npm/v/serialize-javascript.svg?style=flat-square | |
| [express-state]: https://github.com/yahoo/express-state | |
| [JSON.stringify]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify | |
| [LICENSE]: https://github.com/yahoo/serialize-javascript/blob/main/LICENSE | |
| [worker threads]: https://nodejs.org/api/worker_threads.html | |
| [closed-over variables]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures |