diff --git a/README.md b/README.md index c6f5666..95bfbba 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ By default, it is able to stringify and parse objects that contains `BigInt` num * [Installation](#installation) * [Example of use](#example-of-use) +* [Using CircularJSON](#using-circularjson) * [Adding custom types](#adding-custom-types) * [Contributing](#contributing) * [Code of Conduct](#code-of-conduct) @@ -30,6 +31,20 @@ const str = Vilanova.stringify(obj); const newObj = Vilanova.parse(str); ``` +## Using CircularJSON + +Vilanova allows you to use your own stringify and parse functions. One of the most used is CircularJSON. + +```javascript +const CircularJSON = require('circular-json'); +const Vilanova = require('vilanova'); + +const obj = { id: 1, name: 'name', value: 123456789012345678901234567890n }; +const str = Vilanova.stringify(obj, CircularJSON.strinfigy); +const newObj = Vilanova.parse(str, CircularJSON.parse); +``` + + ## Adding custom types To add a custom type, you can use `Vilanova.addType` method. You must provide the name of the type, the reviver function and the replacer function. diff --git a/lib/index.js b/lib/index.js index e0e0509..8d29463 100644 --- a/lib/index.js +++ b/lib/index.js @@ -9,7 +9,7 @@ class Vilanova { * @param {Function} reviver - Function for the reviver of this type. */ static addType(name, replacer, reviver) { - Vilanova.types[name.toLowerCase()] = { replacer, reviver }; + Vilanova.types[name] = { replacer, reviver }; } /** @@ -17,7 +17,7 @@ class Vilanova { */ static addDefaultTypes() { // eslint-disable-next-line - Vilanova.addType('bigint', value => `#BigInt:${value}`, value => BigInt(value)); + Vilanova.addType('bigint', value => `#bigint:${value}`, value => BigInt(value)); } /** @@ -29,7 +29,7 @@ class Vilanova { * @returns {Object} Stringified version or the source object. */ static replacer(key, value) { - const type = Vilanova.types[(typeof value).toLowerCase()]; + const type = Vilanova.types[(typeof value)]; if (type) { return type.replacer(value); } @@ -40,10 +40,12 @@ class Vilanova { * Converts a JavaScript value to a JSON string, using replacers for types * that can be intercepted. * @param {Object} obj - Input object. + * @param {Function} fn - Function that performs the stringify, undefined for JSON.stringify. * @returns {String} Stringified version of the object. */ - static stringify(obj) { - return JSON.stringify(obj, Vilanova.replacer); + static stringify(obj, fn) { + const stringifyFn = fn || JSON.stringify; + return stringifyFn(obj, Vilanova.replacer); } /** @@ -78,18 +80,23 @@ class Vilanova { } const token = Vilanova.getToken(value); if (token.type) { - const type = Vilanova.types[token.type.toLowerCase()]; + const type = Vilanova.types[token.type]; if (type) { return type.reviver(token.value); } } return value; } + /** * Parses a JSON string, constructing the JavaScript value or object described by the string. + * @param {String} str - String to be parsed. + * @param {Function} fn - Function for the parse, undefined for default JSON.parse. + * @returns {Object} The object parsed from the string. */ - static parse(str) { - return JSON.parse(str, Vilanova.reviver); + static parse(str, fn) { + const parseFn = fn || JSON.parse; + return parseFn(str, Vilanova.reviver); } } diff --git a/package-lock.json b/package-lock.json index da014f7..bc83ce7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,56 +13,6 @@ "@babel/highlight": "7.0.0-rc.1" } }, - "@babel/generator": { - "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.0.0-beta.44.tgz", - "integrity": "sha512-5xVb7hlhjGcdkKpMXgicAVgx8syK5VJz193k0i/0sLP6DzE6lRrU1K3B/rFefgdo9LPGMAOOOAWW4jycj07ShQ==", - "dev": true, - "requires": { - "@babel/types": "7.0.0-beta.44", - "jsesc": "^2.5.1", - "lodash": "^4.2.0", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - }, - "dependencies": { - "jsesc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.1.tgz", - "integrity": "sha1-5CGiqOINawgZ3yiQj3glJrlt0f4=", - "dev": true - } - } - }, - "@babel/helper-function-name": { - "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.44.tgz", - "integrity": "sha512-MHRG2qZMKMFaBavX0LWpfZ2e+hLloT++N7rfM3DYOMUOGCD8cVjqZpwiL8a0bOX3IYcQev1ruciT0gdFFRTxzg==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "7.0.0-beta.44", - "@babel/template": "7.0.0-beta.44", - "@babel/types": "7.0.0-beta.44" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.44.tgz", - "integrity": "sha512-w0YjWVwrM2HwP6/H3sEgrSQdkCaxppqFeJtAnB23pRiJB5E/O9Yp7JAAeWBl+gGEgmBFinnTyOv2RN7rcSmMiw==", - "dev": true, - "requires": { - "@babel/types": "7.0.0-beta.44" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.44.tgz", - "integrity": "sha512-aQ7QowtkgKKzPGf0j6u77kBMdUFVBKNHw2p/3HX/POt5/oz8ec5cs0GwlgM8Hz7ui5EwJnzyfRmkNF1Nx1N7aA==", - "dev": true, - "requires": { - "@babel/types": "7.0.0-beta.44" - } - }, "@babel/highlight": { "version": "7.0.0-rc.1", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-rc.1.tgz", @@ -74,111 +24,6 @@ "js-tokens": "^3.0.0" } }, - "@babel/template": { - "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.44.tgz", - "integrity": "sha512-w750Sloq0UNifLx1rUqwfbnC6uSUk0mfwwgGRfdLiaUzfAOiH0tHJE6ILQIUi3KYkjiCDTskoIsnfqZvWLBDng==", - "dev": true, - "requires": { - "@babel/code-frame": "7.0.0-beta.44", - "@babel/types": "7.0.0-beta.44", - "babylon": "7.0.0-beta.44", - "lodash": "^4.2.0" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.44.tgz", - "integrity": "sha512-cuAuTTIQ9RqcFRJ/Y8PvTh+paepNcaGxwQwjIDRWPXmzzyAeCO4KqS9ikMvq0MCbRk6GlYKwfzStrcP3/jSL8g==", - "dev": true, - "requires": { - "@babel/highlight": "7.0.0-beta.44" - } - }, - "@babel/highlight": { - "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.44.tgz", - "integrity": "sha512-Il19yJvy7vMFm8AVAh6OZzaFoAd0hbkeMZiX3P5HGD+z7dyI7RzndHB0dg6Urh/VAFfHtpOIzDUSxmY6coyZWQ==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^3.0.0" - } - }, - "babylon": { - "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.44.tgz", - "integrity": "sha512-5Hlm13BJVAioCHpImtFqNOF2H3ieTOHd0fmFGMxOJ9jgeFqeAwsv3u5P5cR7CSeFrkgHsT19DgFJkHV0/Mcd8g==", - "dev": true - } - } - }, - "@babel/traverse": { - "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0-beta.44.tgz", - "integrity": "sha512-UHuDz8ukQkJCDASKHf+oDt3FVUzFd+QYfuBIsiNu/4+/ix6pP/C+uQZJ6K1oEfbCMv/IKWbgDEh7fcsnIE5AtA==", - "dev": true, - "requires": { - "@babel/code-frame": "7.0.0-beta.44", - "@babel/generator": "7.0.0-beta.44", - "@babel/helper-function-name": "7.0.0-beta.44", - "@babel/helper-split-export-declaration": "7.0.0-beta.44", - "@babel/types": "7.0.0-beta.44", - "babylon": "7.0.0-beta.44", - "debug": "^3.1.0", - "globals": "^11.1.0", - "invariant": "^2.2.0", - "lodash": "^4.2.0" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.44.tgz", - "integrity": "sha512-cuAuTTIQ9RqcFRJ/Y8PvTh+paepNcaGxwQwjIDRWPXmzzyAeCO4KqS9ikMvq0MCbRk6GlYKwfzStrcP3/jSL8g==", - "dev": true, - "requires": { - "@babel/highlight": "7.0.0-beta.44" - } - }, - "@babel/highlight": { - "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.44.tgz", - "integrity": "sha512-Il19yJvy7vMFm8AVAh6OZzaFoAd0hbkeMZiX3P5HGD+z7dyI7RzndHB0dg6Urh/VAFfHtpOIzDUSxmY6coyZWQ==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^3.0.0" - } - }, - "babylon": { - "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.44.tgz", - "integrity": "sha512-5Hlm13BJVAioCHpImtFqNOF2H3ieTOHd0fmFGMxOJ9jgeFqeAwsv3u5P5cR7CSeFrkgHsT19DgFJkHV0/Mcd8g==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.44.tgz", - "integrity": "sha512-5eTV4WRmqbaFM3v9gHAIljEQJU4Ssc6fxL61JN+Oe2ga/BwyjzjamwkCVVAQjHGuAX8i0BWo42dshL8eO5KfLQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.2.0", - "to-fast-properties": "^2.0.0" - }, - "dependencies": { - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - } - } - }, "abab": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", @@ -517,58 +362,6 @@ } } }, - "babel-eslint": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-8.2.6.tgz", - "integrity": "sha512-aCdHjhzcILdP8c9lej7hvXKvQieyRt20SF102SIGyY4cUIiw6UaAtK4j2o3dXX74jEmy0TJ0CEhv4fTIM3SzcA==", - "dev": true, - "requires": { - "@babel/code-frame": "7.0.0-beta.44", - "@babel/traverse": "7.0.0-beta.44", - "@babel/types": "7.0.0-beta.44", - "babylon": "7.0.0-beta.44", - "eslint-scope": "3.7.1", - "eslint-visitor-keys": "^1.0.0" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.44.tgz", - "integrity": "sha512-cuAuTTIQ9RqcFRJ/Y8PvTh+paepNcaGxwQwjIDRWPXmzzyAeCO4KqS9ikMvq0MCbRk6GlYKwfzStrcP3/jSL8g==", - "dev": true, - "requires": { - "@babel/highlight": "7.0.0-beta.44" - } - }, - "@babel/highlight": { - "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.44.tgz", - "integrity": "sha512-Il19yJvy7vMFm8AVAh6OZzaFoAd0hbkeMZiX3P5HGD+z7dyI7RzndHB0dg6Urh/VAFfHtpOIzDUSxmY6coyZWQ==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^3.0.0" - } - }, - "babylon": { - "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.44.tgz", - "integrity": "sha512-5Hlm13BJVAioCHpImtFqNOF2H3ieTOHd0fmFGMxOJ9jgeFqeAwsv3u5P5cR7CSeFrkgHsT19DgFJkHV0/Mcd8g==", - "dev": true - }, - "eslint-scope": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", - "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - } - } - }, "babel-generator": { "version": "6.26.1", "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", @@ -1018,9 +811,9 @@ "dev": true }, "circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.5.tgz", + "integrity": "sha512-13YaR6kiz0kBNmIVM87Io8Hp7bWOo4r61vkEANy8iH9R9bc6avud/1FT0SBpqR1RpIQADOh/Q+yHZDA1iL6ysA==", "dev": true }, "class-utils": { @@ -2044,6 +1837,14 @@ "del": "^2.0.2", "graceful-fs": "^4.1.2", "write": "^0.2.1" + }, + "dependencies": { + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "dev": true + } } }, "for-in": { diff --git a/package.json b/package.json index a396785..dc9f66b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vilanova", "description": "JSON stringify/parse pluggable library with support for BigInt", - "version": "0.0.1", + "version": "0.1.0", "main": "lib/index.js", "keywords": [ "JSON", @@ -39,6 +39,7 @@ "license": "MIT", "dependencies": {}, "devDependencies": { + "circular-json": "^0.5.5", "eslint": "^4.10.0", "eslint-config-airbnb-base": "^12.1.0", "eslint-plugin-import": "^2.8.0", diff --git a/test/vilanova.test.js b/test/vilanova.test.js index da1a4b9..1298ceb 100644 --- a/test/vilanova.test.js +++ b/test/vilanova.test.js @@ -1,22 +1,30 @@ const Vilanova = require('../lib'); +const CircularJSON = require('circular-json'); -function generateStep(i) { +function generateStep(i, circular = false) { const obj = { id: i, name: `name ${i}`, bigValue: BigInt('1000000000000000000000000000000') + BigInt(`${i}`), }; - const str = `{"id":${i},"name":"name ${i}","bigValue":"#BigInt:${obj.bigValue}"}`; + if (circular) { + obj.ref = obj; + } + const str = circular ? `{"id":${i},"name":"name ${i}","bigValue":"#bigint:${obj.bigValue}","ref":"~${i}"}` + : `{"id":${i},"name":"name ${i}","bigValue":"#bigint:${obj.bigValue}"}`; return { obj, str }; } -function generateTestcase(numElements) { +function generateTestcase(numElements, circular = false) { if (!numElements) { return generateStep(1); } const result = { obj: [], str: '' }; for (let i = 0; i < numElements; i += 1) { - const step = generateStep(i); + const step = generateStep(i, circular); + if (circular) { + step.obj.ref = step.obj; + } result.obj.push(step.obj); result.str = i > 0 ? `${result.str},${step.str}` : `[${step.str}`; } @@ -28,7 +36,7 @@ describe('Vilanova', () => { describe('Stringify', () => { test('It should stringify a BigInt', () => { const input = BigInt('123456789012345678901234567890'); - const expected = '"#BigInt:123456789012345678901234567890"'; + const expected = '"#bigint:123456789012345678901234567890"'; const actual = Vilanova.stringify(input); expect(actual).toEqual(expected); }); @@ -42,6 +50,11 @@ describe('Vilanova', () => { const actual = Vilanova.stringify(testcase.obj); expect(actual).toEqual(testcase.str); }); + test('It should allow to pass a custom stringify function', () => { + const testcase = generateTestcase(100, true); + const actual = Vilanova.stringify(testcase.obj, CircularJSON.stringify); + expect(actual).toEqual(testcase.str); + }); }); describe('Get Token', () => { @@ -64,20 +77,20 @@ describe('Vilanova', () => { expect(actual).toEqual(expected); }); test('It should return the source if does not start with #', () => { - const input = 'BigInt:123456'; + const input = 'bigint:123456'; const expected = { type: undefined, value: input }; const actual = Vilanova.getToken(input); expect(actual).toEqual(expected); }); test('It should return the source if does not contains :', () => { - const input = '#BigInt.123456'; + const input = '#bigint.123456'; const expected = { type: undefined, value: input }; const actual = Vilanova.getToken(input); expect(actual).toEqual(expected); }); test('It should pop the type if the format is correct', () => { - const input = '#BigInt:123456'; - const expected = { type: 'BigInt', value: '123456' }; + const input = '#bigint:123456'; + const expected = { type: 'bigint', value: '123456' }; const actual = Vilanova.getToken(input); expect(actual).toEqual(expected); }); @@ -85,7 +98,7 @@ describe('Vilanova', () => { describe('Parse', () => { test('It should parse a BigInt', () => { - const input = '"#BigInt:123456789012345678901234567890"'; + const input = '"#bigint:123456789012345678901234567890"'; const expected = BigInt('123456789012345678901234567890'); const actual = Vilanova.parse(input); expect(actual).toEqual(expected); @@ -106,5 +119,11 @@ describe('Vilanova', () => { const actual = Vilanova.parse(testcase.str); expect(actual).toEqual(testcase.obj); }); + test('It should allow to pass a custom parse function', () => { + const testcase = generateTestcase(100, true); + const actual = Vilanova.parse(testcase.str, CircularJSON.parse); + expect(actual).toEqual(testcase.obj); + }); }); + });