Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: yahoo/serialize-javascript
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 6c43b02710bab8ddaf0d86dd2f9602af74ade7fc
Choose a base ref
...
head repository: yahoo/serialize-javascript
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: b71ec23841d7cf30847d3071d9da38ee0b397fc8
Choose a head ref
Loading
Showing with 2,328 additions and 1,161 deletions.
  1. +8 −0 .github/dependabot.yml
  2. +22 −0 .github/workflows/test.yml
  3. +2 −1 .npmignore
  4. +0 −4 .travis.yml
  5. +12 −12 README.md
  6. +63 −5 index.js
  7. +2,093 −1,127 package-lock.json
  8. +6 −3 package.json
  9. +122 −9 test/unit/serialize.js
8 changes: 8 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: monthly
time: "13:00"
open-pull-requests-limit: 10
22 changes: 22 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Test

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
3 changes: 2 additions & 1 deletion .npmignore
Original file line number Diff line number Diff line change
@@ -3,5 +3,6 @@ coverage/
test/
.gitignore
.npmignore
.travis.yml
.nyc_output
.vscode
.github
4 changes: 0 additions & 4 deletions .travis.yml

This file was deleted.

24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ Serialize JavaScript to a _superset_ of JSON that includes regular expressions,

[![npm Version][npm-badge]][npm]
[![Dependency Status][david-badge]][david]
[![Build Status][travis-badge]][travis]
![Test](https://github.com/yahoo/serialize-javascript/workflows/Test/badge.svg)

## Overview

@@ -40,22 +40,24 @@ serialize({
bool : true,
nil : null,
undef: undefined,
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
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,"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":/([^\s]+)/g}'
'{"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 produced a beautified string, you can pass an optional second argument to `serialize()` to define the number of spaces to be used for the indentation.
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

@@ -136,8 +138,6 @@ See the [LICENSE file][LICENSE] for license text and copyright information.
[npm-badge]: https://img.shields.io/npm/v/serialize-javascript.svg?style=flat-square
[david]: https://david-dm.org/yahoo/serialize-javascript
[david-badge]: https://img.shields.io/david/yahoo/serialize-javascript.svg?style=flat-square
[travis]: https://travis-ci.org/yahoo/serialize-javascript
[travis-badge]: https://img.shields.io/travis/yahoo/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/master/LICENSE
[LICENSE]: https://github.com/yahoo/serialize-javascript/blob/main/LICENSE
68 changes: 63 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
@@ -6,9 +6,12 @@ See the accompanying LICENSE file for terms.

'use strict';

var randomBytes = require('randombytes');

// Generate an internal UID to make the regexp pattern harder to guess.
var UID = Math.floor(Math.random() * 0x10000000000).toString(16);
var PLACE_HOLDER_REGEXP = new RegExp('"@__(F|R|D|M|S|U)-' + UID + '-(\\d+)__@"', 'g');
var UID_LENGTH = 16;
var UID = generateUID();
var PLACE_HOLDER_REGEXP = new RegExp('(\\\\)?"@__(F|R|D|M|S|A|U|I|B|L)-' + UID + '-(\\d+)__@"', 'g');

var IS_NATIVE_CODE_REGEXP = /\{\s*\[native code\]\s*\}/g;
var IS_PURE_FUNCTION = /function.*?\(/;
@@ -31,6 +34,15 @@ function escapeUnsafeChars(unsafeChar) {
return ESCAPED_CHARS[unsafeChar];
}

function generateUID() {
var bytes = randomBytes(UID_LENGTH);
var result = '';
for(var i=0; i<UID_LENGTH; ++i) {
result += bytes[i].toString(16);
}
return result;
}

function deleteFunctions(obj){
var functionKeys = [];
for (var key in obj) {
@@ -56,7 +68,11 @@ module.exports = function serialize(obj, options) {
var dates = [];
var maps = [];
var sets = [];
var arrays = [];
var undefs = [];
var infinities= [];
var bigInts = [];
var urls = [];

// Returns placeholders for functions and regexps (identified by index)
// which are later replaced by their string representation.
@@ -67,7 +83,7 @@ module.exports = function serialize(obj, options) {
deleteFunctions(value);
}

if (!value && value !== undefined) {
if (!value && value !== undefined && value !== BigInt(0)) {
return value;
}

@@ -92,6 +108,17 @@ module.exports = function serialize(obj, options) {
if(origValue instanceof Set) {
return '@__S-' + UID + '-' + (sets.push(origValue) - 1) + '__@';
}

if(origValue instanceof Array) {
var isSparse = origValue.filter(function(){return true}).length !== origValue.length;
if (isSparse) {
return '@__A-' + UID + '-' + (arrays.push(origValue) - 1) + '__@';
}
}

if(origValue instanceof URL) {
return '@__L-' + UID + '-' + (urls.push(origValue) - 1) + '__@';
}
}

if (type === 'function') {
@@ -102,6 +129,14 @@ module.exports = function serialize(obj, options) {
return '@__U-' + UID + '-' + (undefs.push(origValue) - 1) + '__@';
}

if (type === 'number' && !isNaN(origValue) && !isFinite(origValue)) {
return '@__I-' + UID + '-' + (infinities.push(origValue) - 1) + '__@';
}

if (type === 'bigint') {
return '@__B-' + UID + '-' + (bigInts.push(origValue) - 1) + '__@';
}

return value;
}

@@ -175,14 +210,21 @@ module.exports = function serialize(obj, options) {
str = str.replace(UNSAFE_CHARS_REGEXP, escapeUnsafeChars);
}

if (functions.length === 0 && regexps.length === 0 && dates.length === 0 && maps.length === 0 && sets.length === 0 && undefs.length === 0) {
if (functions.length === 0 && regexps.length === 0 && dates.length === 0 && maps.length === 0 && sets.length === 0 && arrays.length === 0 && undefs.length === 0 && infinities.length === 0 && bigInts.length === 0 && urls.length === 0) {
return str;
}

// Replaces all occurrences of function, regexp, date, map and set placeholders in the
// JSON string with their string representations. If the original value can
// not be found, then `undefined` is used.
return str.replace(PLACE_HOLDER_REGEXP, function (match, type, valueIndex) {
return str.replace(PLACE_HOLDER_REGEXP, function (match, backSlash, type, valueIndex) {
// The placeholder may not be preceded by a backslash. This is to prevent
// replacing things like `"a\"@__R-<UID>-0__@"` and thus outputting
// invalid JS.
if (backSlash) {
return match;
}

if (type === 'D') {
return "new Date(\"" + dates[valueIndex].toISOString() + "\")";
}
@@ -199,10 +241,26 @@ module.exports = function serialize(obj, options) {
return "new Set(" + serialize(Array.from(sets[valueIndex].values()), options) + ")";
}

if (type === 'A') {
return "Array.prototype.slice.call(" + serialize(Object.assign({ length: arrays[valueIndex].length }, arrays[valueIndex]), options) + ")";
}

if (type === 'U') {
return 'undefined'
}

if (type === 'I') {
return infinities[valueIndex];
}

if (type === 'B') {
return "BigInt(\"" + bigInts[valueIndex] + "\")";
}

if (type === 'L') {
return "new URL(" + serialize(urls[valueIndex].toString(), options) + ")";
}

var fn = functions[valueIndex];

return serializeFunc(fn);
Loading