Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

json-stable-stringify

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

json-stable-stringify - npm Package Compare versions

Comparing version 1.1.1 to 1.2.0

index.d.ts

23

CHANGELOG.md

@@ -8,2 +8,25 @@ # Changelog

## [v1.2.0](https://github.com/ljharb/json-stable-stringify/compare/v1.1.1...v1.2.0) - 2024-12-17
### Fixed
- [readme] remove dead badges [`#14`](https://github.com/ljharb/json-stable-stringify/issues/14)
### Commits
- [New] add types [`5dbd6c8`](https://github.com/ljharb/json-stable-stringify/commit/5dbd6c802fe013082e597ecf6a8c3428e60d906b)
- [eslint] clean up formatting [`21e95e5`](https://github.com/ljharb/json-stable-stringify/commit/21e95e57ea55c6b7e8c63835391b1791a8ed9323)
- [meta] sort package.json [`a9f44d5`](https://github.com/ljharb/json-stable-stringify/commit/a9f44d5e532e93e7cc48e384472c0a9da189bab9)
- [actions] split out node 10-20, and 20+ [`74551e4`](https://github.com/ljharb/json-stable-stringify/commit/74551e4cc76ae90880b0471365de47d1c3dd1379)
- [Tests] add test coverage for options provided directly on a cmp function [`0a50205`](https://github.com/ljharb/json-stable-stringify/commit/0a502052b9191f53f072599aac61aa829ac9e0ae)
- [Robustness] cache more builtins [`d390c99`](https://github.com/ljharb/json-stable-stringify/commit/d390c99889ec80a20b77a9f73d8d8134f0fc11b8)
- [Dev Deps] update `@ljharb/eslint-config`, `auto-changelog`, `tape` [`03686a0`](https://github.com/ljharb/json-stable-stringify/commit/03686a0af26444bac661e8ca0e70e3d13a4938d0)
- [Tests] key ordering is reversed in node 11+ [`7034a17`](https://github.com/ljharb/json-stable-stringify/commit/7034a176d0dde8df1f899bf0f1c44e73f5792947)
- [Dev Deps] update `npmignore`, `tape` [`ba8d519`](https://github.com/ljharb/json-stable-stringify/commit/ba8d519505f59725c9f4b0451332f6d64801a910)
- [Refactor] use `call-bound` directly [`850b24c`](https://github.com/ljharb/json-stable-stringify/commit/850b24c5b3dc69b59804637c92c10f7bb3277ab8)
- [Tests] replace `aud` with `npm audit` [`22fb720`](https://github.com/ljharb/json-stable-stringify/commit/22fb72061005f9b124a0dc84f8b87c3a977c00bd)
- [Deps] update `call-bind` [`adc30b0`](https://github.com/ljharb/json-stable-stringify/commit/adc30b0746b58d469492e7586b1d32469dce4783)
- [Deps] update `call-bind` [`a280582`](https://github.com/ljharb/json-stable-stringify/commit/a280582e6b8bb6e04642010931b60f9fda2fa0df)
- [Dev Deps] add missing peer dep [`3bb517c`](https://github.com/ljharb/json-stable-stringify/commit/3bb517cc179cd90e841581046791d24cc2bee66a)
## [v1.1.1](https://github.com/ljharb/json-stable-stringify/compare/v1.1.0...v1.1.1) - 2024-01-16

@@ -10,0 +33,0 @@

3

example/value_cmp.js

@@ -7,3 +7,4 @@ 'use strict';

var s = stringify(obj, function (a, b) {
var s = stringify(obj, /** @type {import('..').Comparator} */ function (a, b) {
// @ts-expect-error implicit coercion here is fine
return a.value < b.value ? 1 : -1;

@@ -10,0 +11,0 @@ });

'use strict';
/** @type {typeof JSON.stringify} */
var jsonStringify = (typeof JSON !== 'undefined' ? JSON : require('jsonify')).stringify;

@@ -8,7 +9,10 @@

var callBind = require('call-bind');
var callBound = require('call-bind/callBound');
var callBound = require('call-bound');
var $join = callBound('Array.prototype.join');
var $push = callBound('Array.prototype.push');
var $indexOf = callBound('Array.prototype.indexOf');
var $splice = callBound('Array.prototype.splice');
var $sort = callBound('Array.prototype.sort');
/** @type {(n: number, char: string) => string} */
var strRepeat = function repeat(n, char) {

@@ -22,5 +26,8 @@ var str = '';

var defaultReplacer = function (parent, key, value) { return value; };
/** @type {(parent: import('.').Node, key: import('.').Key, value: unknown) => unknown} */
var defaultReplacer = function (_parent, _key, value) { return value; };
/** @type {import('.')} */
module.exports = function stableStringify(obj) {
/** @type {Parameters<import('.')>[1]} */
var opts = arguments.length > 1 ? arguments[1] : void undefined;

@@ -30,12 +37,18 @@ var space = (opts && opts.space) || '';

var cycles = !!opts && typeof opts.cycles === 'boolean' && opts.cycles;
/** @type {undefined | typeof defaultReplacer} */
var replacer = opts && opts.replacer ? callBind(opts.replacer) : defaultReplacer;
var cmpOpt = typeof opts === 'function' ? opts : opts && opts.cmp;
/** @type {undefined | (<T extends import('.').NonArrayNode>(node: T) => (a: Exclude<keyof T, symbol | number>, b: Exclude<keyof T, symbol | number>) => number)} */
var cmp = cmpOpt && function (node) {
var get = cmpOpt.length > 2 && function get(k) { return node[k]; };
// eslint-disable-next-line no-extra-parens
var get = /** @type {NonNullable<typeof cmpOpt>} */ (cmpOpt).length > 2
&& /** @type {import('.').Getter['get']} */ function get(k) { return node[k]; };
return function (a, b) {
return cmpOpt(
// eslint-disable-next-line no-extra-parens
return /** @type {NonNullable<typeof cmpOpt>} */ (cmpOpt)(
{ key: a, value: node[a] },
{ key: b, value: node[b] },
get ? { __proto__: null, get: get } : void undefined
// @ts-expect-error TS doesn't understand the optimization used here
get ? /** @type {import('.').Getter} */ { __proto__: null, get: get } : void undefined
);

@@ -45,51 +58,60 @@ };

/** @type {import('.').Node[]} */
var seen = [];
return (function stringify(parent, key, node, level) {
var indent = space ? '\n' + strRepeat(level, space) : '';
var colonSeparator = space ? ': ' : ':';
return (/** @type {(parent: import('.').Node, key: string | number, node: unknown, level: number) => string | undefined} */
function stringify(parent, key, node, level) {
var indent = space ? '\n' + strRepeat(level, space) : '';
var colonSeparator = space ? ': ' : ':';
if (node && node.toJSON && typeof node.toJSON === 'function') {
node = node.toJSON();
}
// eslint-disable-next-line no-extra-parens
if (node && /** @type {{ toJSON?: unknown }} */ (node).toJSON && typeof /** @type {{ toJSON?: unknown }} */ (node).toJSON === 'function') {
// eslint-disable-next-line no-extra-parens
node = /** @type {{ toJSON: Function }} */ (node).toJSON();
}
node = replacer(parent, key, node);
node = replacer(parent, key, node);
if (node === undefined) {
return;
}
if (typeof node !== 'object' || node === null) {
return jsonStringify(node);
}
if (isArray(node)) {
var out = [];
for (var i = 0; i < node.length; i++) {
var item = stringify(node, i, node[i], level + 1) || jsonStringify(null);
$push(out, indent + space + item);
if (node === undefined) {
return;
}
return '[' + $join(out, ',') + indent + ']';
}
if (typeof node !== 'object' || node === null) {
return jsonStringify(node);
}
if (isArray(node)) {
var out = [];
for (var i = 0; i < node.length; i++) {
var item = stringify(node, i, node[i], level + 1) || jsonStringify(null);
out[out.length] = indent + space + item;
}
return '[' + $join(out, ',') + indent + ']';
}
if (seen.indexOf(node) !== -1) {
if (cycles) { return jsonStringify('__cycle__'); }
throw new TypeError('Converting circular structure to JSON');
} else { $push(seen, node); }
if ($indexOf(seen, node) !== -1) {
if (cycles) { return jsonStringify('__cycle__'); }
throw new TypeError('Converting circular structure to JSON');
} else {
seen[seen.length] = /** @type {import('.').NonArrayNode} */ (node);
}
var keys = objectKeys(node).sort(cmp && cmp(node));
var out = [];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var value = stringify(node, key, node[key], level + 1);
/** @type {import('.').Key[]} */
// eslint-disable-next-line no-extra-parens
var keys = $sort(objectKeys(node), cmp && cmp(/** @type {import('.').NonArrayNode} */ (node)));
var out = [];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
// eslint-disable-next-line no-extra-parens
var value = stringify(/** @type {import('.').Node} */ (node), key, /** @type {import('.').NonArrayNode} */ (node)[key], level + 1);
if (!value) { continue; }
if (!value) { continue; }
var keyValue = jsonStringify(key)
+ colonSeparator
+ value;
var keyValue = jsonStringify(key)
+ colonSeparator
+ value;
$push(out, indent + space + keyValue);
}
seen.splice(seen.indexOf(node), 1);
return '{' + $join(out, ',') + indent + '}';
}({ '': obj }, '', obj, 0));
out[out.length] = indent + space + keyValue;
}
$splice(seen, $indexOf(seen, node), 1);
return '{' + $join(out, ',') + indent + '}';
}({ '': obj }, '', obj, 0)
);
};
{
"name": "json-stable-stringify",
"version": "1.1.1",
"version": "1.2.0",
"description": "deterministic JSON.stringify() with custom sorting to get deterministic hashes from stringified results",
"main": "index.js",
"dependencies": {
"call-bind": "^1.0.5",
"isarray": "^2.0.5",
"jsonify": "^0.0.1",
"object-keys": "^1.1.1"
},
"devDependencies": {
"@ljharb/eslint-config": "^21.1.0",
"aud": "^2.0.4",
"auto-changelog": "^2.4.0",
"eslint": "=8.8.0",
"in-publish": "^2.0.1",
"npmignore": "^0.3.0",
"safe-publish-latest": "^2.0.0",
"tape": "^5.7.3"
},
"scripts": {

@@ -27,21 +11,10 @@ "prepack": "npmignore --auto --commentLines=autogenerated",

"lint": "eslint --ext=js,mjs .",
"postlint": "tsc && attw -P",
"pretest": "npm run lint",
"tests-only": "tape 'test/**/*.js'",
"test": "npm run tests-only",
"posttest": "aud --production",
"posttest": "npx npm@'>= 10.2' audit --production",
"version": "auto-changelog && git add CHANGELOG.md",
"postversion": "auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""
},
"testling": {
"files": "test/*.js",
"browsers": [
"ie/8..latest",
"ff/5",
"ff/latest",
"chrome/15",
"chrome/latest",
"safari/latest",
"opera/latest"
]
},
"repository": {

@@ -51,3 +24,2 @@ "type": "git",

},
"homepage": "https://github.com/ljharb/json-stable-stringify",
"keywords": [

@@ -66,9 +38,40 @@ "json",

},
"license": "MIT",
"bugs": {
"url": "https://github.com/ljharb/json-stable-stringify/issues"
},
"homepage": "https://github.com/ljharb/json-stable-stringify",
"funding": {
"url": "https://github.com/sponsors/ljharb"
},
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.8",
"call-bound": "^1.0.3",
"isarray": "^2.0.5",
"jsonify": "^0.0.1",
"object-keys": "^1.1.1"
},
"devDependencies": {
"@arethetypeswrong/cli": "^0.17.1",
"@ljharb/eslint-config": "^21.1.1",
"@ljharb/tsconfig": "^0.2.2",
"@types/call-bind": "^1.0.5",
"@types/isarray": "^2.0.3",
"@types/object-keys": "^1.0.3",
"@types/tape": "^5.7.0",
"auto-changelog": "^2.5.0",
"encoding": "^0.1.13",
"eslint": "=8.8.0",
"in-publish": "^2.0.1",
"npmignore": "^0.3.1",
"safe-publish-latest": "^2.0.0",
"tape": "^5.9.0",
"typescript": "next"
},
"engines": {
"node": ">= 0.4"
},
"testling": {
"files": "test/*.js"
},
"auto-changelog": {

@@ -84,5 +87,6 @@ "output": "CHANGELOG.md",

"ignore": [
".github/workflows"
".github/workflows",
"types"
]
}
}

@@ -14,6 +14,2 @@ # json-stable-stringify <sup>[![Version Badge][npm-version-svg]][package-url]</sup>

[![browser support](https://ci.testling.com/ljharb/json-stable-stringify.png)](https://ci.testling.com/ljharb/json-stable-stringify)
[![build status](https://secure.travis-ci.org/ljharb/json-stable-stringify.png)](http://travis-ci.org/ljharb/json-stable-stringify)
# example

@@ -20,0 +16,0 @@

@@ -18,4 +18,5 @@ 'use strict';

stringify({ a: 1, b: 2 }, function (a, b) { // eslint-disable-line no-unused-vars
stringify({ a: 1, b: 2 }, /** @type {import('..').Comparator} */ function (_a, _b) { // eslint-disable-line no-unused-vars
t.equal(arguments[2], undefined, 'comparator options not passed when not explicitly requested');
return NaN;
});

@@ -26,3 +27,5 @@

var get = options.get;
// @ts-expect-error implicit coercion here is fine
var v1 = (get('!' + a.key) || 0) + a.value;
// @ts-expect-error implicit coercion here is fine
var v2 = (get('!' + b.key) || 0) + b.value;

@@ -29,0 +32,0 @@ return v1 - v2;

@@ -14,3 +14,3 @@ 'use strict';

t.plan(1);
var one = { a: 1 };
var one = { a: 1, two: {} };
var two = { a: 2, one: one };

@@ -21,3 +21,7 @@ one.two = two;

} catch (ex) {
t.equal(ex.toString(), 'TypeError: Converting circular structure to JSON');
if (ex == null) { // eslint-disable-line eqeqeq
t.fail('nullish exception');
} else {
t.equal(ex.toString(), 'TypeError: Converting circular structure to JSON');
}
}

@@ -28,3 +32,3 @@ });

t.plan(1);
var one = { a: 1 };
var one = { a: 1, two: {} };
var two = { a: 2, one: one };

@@ -31,0 +35,0 @@ one.two = two;

@@ -19,3 +19,4 @@ 'use strict';

var obj = { a: 1, b: 2, c: false };
var replacer = function (key, value) {
/** @type {import('..').StableStringifyOptions['replacer']} */
var replacer = function (_key, value) {
if (value === 1) { return 'one'; }

@@ -33,2 +34,3 @@ if (value === 2) { return 'two'; }

var obj = { a: 1, b: 2, c: false };
/** @type {import('..').StableStringifyOptions['replacer']} */
var replacer = function (key, value) {

@@ -47,3 +49,4 @@ if (key === 'b') { return { d: 1 }; }

var obj = { a: 1, b: 2, c: false };
var replacer = function (key, value) {
/** @type {import('..').StableStringifyOptions['replacer']} */
var replacer = function (_key, value) {
if (value === false) { return; }

@@ -60,2 +63,3 @@ return value;

var obj = { a: 1, b: 2, c: false };
/** @type {import('..').StableStringifyOptions['replacer']} */
var replacer = function (key, value) {

@@ -73,3 +77,4 @@ if (key === 'b') { return ['one', 'two']; }

var obj = { a: 1, b: 2, c: [1, 2] };
var replacer = function (key, value) {
/** @type {import('..').StableStringifyOptions['replacer']} */
var replacer = function (_key, value) {
if (value === 1) { return 'one'; }

@@ -76,0 +81,0 @@ if (value === 2) { return 'two'; }

@@ -6,2 +6,5 @@ 'use strict';

// @ts-expect-error node ensures this will never fail
var isNode10OrLess = parseInt(process.version.match(/^v(\d+)\./)[1], 10) <= 10;
test('space parameter', function (t) {

@@ -74,1 +77,28 @@ t.plan(1);

});
test('space parameter, on a cmp function', function (t) {
t.plan(3);
var obj = { one: 1, two: 2 };
/** @type {import('..').Comparator & import('../').StableStringifyOptions} */
var cmp = function (a, b) {
return (a < b ? 1 : -1) * (isNode10OrLess ? -1 : 1);
};
t.equal(
stringify(obj, { space: '\t' }),
'{\n\t"one": 1,\n\t"two": 2\n}',
'no cmp option (control)'
);
t.equal(
stringify(obj, { cmp: cmp, space: '\t' }),
'{\n\t"two": 2,\n\t"one": 1\n}',
'cmp option in the object'
);
cmp.space = '\t';
t.equal(
stringify(obj, cmp),
'{\n\t"two": 2,\n\t"one": 1\n}',
'cmp passed directly, with space option on it'
);
});

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc