@brillout/json-serializer 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE.md ADDED
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022-present Romuald Brillout
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,2 @@
1
+ export { parse };
2
+ declare function parse(str: string): unknown;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parse = void 0;
4
+ const types_1 = require("./types");
5
+ function parse(str) {
6
+ // We don't use the reviver option in `JSON.parse(str, reviver)` because it doesn't support `undefined` values
7
+ const value = JSON.parse(str);
8
+ return modifier(value);
9
+ }
10
+ exports.parse = parse;
11
+ function modifier(value) {
12
+ if (typeof value === 'string') {
13
+ return reviver(value);
14
+ }
15
+ if (typeof value === 'object' && value !== null) {
16
+ Object.entries(value).forEach(([key, val]) => {
17
+ ;
18
+ value[key] = modifier(val);
19
+ });
20
+ }
21
+ return value;
22
+ }
23
+ function reviver(value) {
24
+ for (const { match, deserialize } of types_1.types) {
25
+ if (match(value)) {
26
+ return deserialize(value, parse);
27
+ }
28
+ }
29
+ return value;
30
+ }
@@ -0,0 +1,7 @@
1
+ export { stringify };
2
+ declare function stringify(value: unknown, { forbidReactElements, space, valueName, sortObjectKeys }?: {
3
+ forbidReactElements?: boolean;
4
+ space?: number;
5
+ valueName?: string;
6
+ sortObjectKeys?: boolean;
7
+ }): string;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stringify = void 0;
4
+ const types_1 = require("./types");
5
+ const isReactElement_1 = require("./utils/isReactElement");
6
+ const isCallable_1 = require("./utils/isCallable");
7
+ const isObject_1 = require("./utils/isObject");
8
+ function stringify(value, { forbidReactElements, space, valueName = 'value', sortObjectKeys } = {}) {
9
+ const path = [];
10
+ // The only error `JSON.stringify()` can throw is `TypeError "cyclic object value"`.
11
+ // - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#exceptions
12
+ // - This means we have total of 3 possible errors while serializing:
13
+ // - Cyclic references
14
+ // - Functions
15
+ // - React elements
16
+ const serializer = (val) => JSON.stringify(val, replacer, space);
17
+ return serializer(value);
18
+ function replacer(key, value) {
19
+ if (key !== '') {
20
+ path.push(key);
21
+ }
22
+ if (forbidReactElements && (0, isReactElement_1.isReactElement)(value)) {
23
+ throw new Error(genErrMsg('React element'));
24
+ }
25
+ if ((0, isCallable_1.isCallable)(value)) {
26
+ const functionName = value.name;
27
+ throw new Error(genErrMsg('function', path.length === 0 ? functionName : undefined));
28
+ }
29
+ const valueOriginal = this[key];
30
+ for (const { is, serialize } of types_1.types.slice().reverse()) {
31
+ if (is(valueOriginal)) {
32
+ //@ts-ignore
33
+ return serialize(valueOriginal, serializer);
34
+ }
35
+ }
36
+ if (sortObjectKeys && (0, isObject_1.isObject)(value)) {
37
+ const copy = {};
38
+ Object.keys(value)
39
+ .sort()
40
+ .forEach((key) => {
41
+ copy[key] = value[key];
42
+ });
43
+ value = copy;
44
+ }
45
+ return value;
46
+ }
47
+ function genErrMsg(valueType, valName) {
48
+ const name = valName ? ' `' + valName + '`' : '';
49
+ const location = path.length === 0 ? '' : ` ${name ? 'at ' : ''}\`${valueName}[${path.map((p) => `'${p}'`).join('][')}]\``;
50
+ const fallback = name === '' && location === '' ? ` ${valueName}` : '';
51
+ return `Cannot serialize${name}${location}${fallback} because it is a ${valueType} (https://github.com/brillout/json-s)`;
52
+ }
53
+ }
54
+ exports.stringify = stringify;
@@ -0,0 +1,8 @@
1
+ export { types };
2
+ declare const types: readonly [Type<undefined, unknown>, Type<number, unknown>, Type<number, unknown>, Type<number, unknown>, Type<Date, any>, Type<BigInt, any>, Type<RegExp, any>, Type<Map<any, any>, any[]>, Type<Set<unknown>, unknown[]>, Type<string, any>];
3
+ declare type Type<T, IntermediateType> = {
4
+ is: (val: unknown) => asserts val is T;
5
+ match: (str: string) => boolean;
6
+ serialize: (val: T, serializer: (val: IntermediateType) => string) => string;
7
+ deserialize: (str: string, deserializer: (str: string) => IntermediateType) => T;
8
+ };
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.types = void 0;
4
+ const types = [
5
+ ts({
6
+ is: (val) => val === undefined,
7
+ match: (str) => str === '!undefined',
8
+ serialize: () => '!undefined',
9
+ deserialize: () => undefined
10
+ }),
11
+ ts({
12
+ is: (val) => val === Infinity,
13
+ match: (str) => str === '!Infinity',
14
+ serialize: () => '!Infinity',
15
+ deserialize: () => Infinity
16
+ }),
17
+ ts({
18
+ is: (val) => val === -Infinity,
19
+ match: (str) => str === '!-Infinity',
20
+ serialize: () => '!-Infinity',
21
+ deserialize: () => -Infinity
22
+ }),
23
+ ts({
24
+ is: (val) => typeof val === 'number' && isNaN(val),
25
+ match: (str) => str === '!NaN',
26
+ serialize: () => '!NaN',
27
+ deserialize: () => NaN
28
+ }),
29
+ ts({
30
+ is: (val) => val instanceof Date,
31
+ match: (str) => str.startsWith('!Date:'),
32
+ serialize: (val) => '!Date:' + val.toISOString(),
33
+ deserialize: (str) => new Date(str.slice('!Date:'.length))
34
+ }),
35
+ ts({
36
+ is: (val) => typeof val === 'bigint',
37
+ match: (str) => str.startsWith('!BigInt:'),
38
+ serialize: (val) => '!BigInt:' + val.toString(),
39
+ deserialize: (str) => {
40
+ if (typeof BigInt === 'undefined') {
41
+ throw new Error('Your JavaScript environement does not support BigInt. Consider adding a polyfill.');
42
+ }
43
+ return BigInt(str.slice('!BigInt:'.length));
44
+ }
45
+ }),
46
+ ts({
47
+ is: (val) => val instanceof RegExp,
48
+ match: (str) => str.startsWith('!RegExp:'),
49
+ serialize: (val) => '!RegExp:' + val.toString(),
50
+ deserialize: (str) => {
51
+ str = str.slice('!RegExp:'.length);
52
+ // const args: string[] = str.match(/\/(.*?)\/([gimy])?$/)!
53
+ const args = str.match(/\/(.*)\/(.*)?/);
54
+ const pattern = args[1];
55
+ const flags = args[2];
56
+ return new RegExp(pattern, flags);
57
+ }
58
+ }),
59
+ ts({
60
+ is: (val) => val instanceof Map,
61
+ match: (str) => str.startsWith('!Map:'),
62
+ serialize: (val, serializer) => '!Map:' + serializer(Array.from(val.entries())),
63
+ deserialize: (str, deserializer) => new Map(deserializer(str.slice('!Map:'.length)))
64
+ }),
65
+ ts({
66
+ is: (val) => val instanceof Set,
67
+ match: (str) => str.startsWith('!Set:'),
68
+ serialize: (val, serializer) => '!Set:' + serializer(Array.from(val.values())),
69
+ deserialize: (str, deserializer) => new Set(deserializer(str.slice('!Set:'.length)))
70
+ }),
71
+ // Avoid collisions with the special strings defined above
72
+ ts({
73
+ is: (val) => typeof val === 'string' && val.startsWith('!'),
74
+ match: (str) => str.startsWith('!'),
75
+ serialize: (val) => '!' + val,
76
+ deserialize: (str) => str.slice(1)
77
+ })
78
+ ];
79
+ exports.types = types;
80
+ // Type check
81
+ function ts(t) {
82
+ return t;
83
+ }
@@ -0,0 +1 @@
1
+ export declare function isCallable<T extends (...args: unknown[]) => unknown>(thing: T | unknown): thing is T;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isCallable = void 0;
4
+ function isCallable(thing) {
5
+ return thing instanceof Function || typeof thing === 'function';
6
+ }
7
+ exports.isCallable = isCallable;
@@ -0,0 +1,3 @@
1
+ export { isObject };
2
+ declare type Object = Record<string, unknown>;
3
+ declare function isObject(value: unknown): value is Object;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isObject = void 0;
4
+ function isObject(value) {
5
+ if (typeof value !== 'object' || value === null) {
6
+ return false;
7
+ }
8
+ if (Array.isArray(value)) {
9
+ return false;
10
+ }
11
+ return true;
12
+ }
13
+ exports.isObject = isObject;
@@ -0,0 +1 @@
1
+ export declare function isReactElement(value: unknown): boolean;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isReactElement = void 0;
4
+ function isReactElement(value) {
5
+ return (typeof value === 'object' &&
6
+ value !== null &&
7
+ String(value['$$typeof']) === 'Symbol(react.element)');
8
+ }
9
+ exports.isReactElement = isReactElement;
@@ -0,0 +1,2 @@
1
+ export { parse };
2
+ declare function parse(str: string): unknown;
@@ -0,0 +1,27 @@
1
+ export { parse };
2
+ import { types } from './types';
3
+ function parse(str) {
4
+ // We don't use the reviver option in `JSON.parse(str, reviver)` because it doesn't support `undefined` values
5
+ const value = JSON.parse(str);
6
+ return modifier(value);
7
+ }
8
+ function modifier(value) {
9
+ if (typeof value === 'string') {
10
+ return reviver(value);
11
+ }
12
+ if (typeof value === 'object' && value !== null) {
13
+ Object.entries(value).forEach(([key, val]) => {
14
+ ;
15
+ value[key] = modifier(val);
16
+ });
17
+ }
18
+ return value;
19
+ }
20
+ function reviver(value) {
21
+ for (const { match, deserialize } of types) {
22
+ if (match(value)) {
23
+ return deserialize(value, parse);
24
+ }
25
+ }
26
+ return value;
27
+ }
@@ -0,0 +1,7 @@
1
+ export { stringify };
2
+ declare function stringify(value: unknown, { forbidReactElements, space, valueName, sortObjectKeys }?: {
3
+ forbidReactElements?: boolean;
4
+ space?: number;
5
+ valueName?: string;
6
+ sortObjectKeys?: boolean;
7
+ }): string;
@@ -0,0 +1,51 @@
1
+ export { stringify };
2
+ import { types } from './types';
3
+ import { isReactElement } from './utils/isReactElement';
4
+ import { isCallable } from './utils/isCallable';
5
+ import { isObject } from './utils/isObject';
6
+ function stringify(value, { forbidReactElements, space, valueName = 'value', sortObjectKeys } = {}) {
7
+ const path = [];
8
+ // The only error `JSON.stringify()` can throw is `TypeError "cyclic object value"`.
9
+ // - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#exceptions
10
+ // - This means we have total of 3 possible errors while serializing:
11
+ // - Cyclic references
12
+ // - Functions
13
+ // - React elements
14
+ const serializer = (val) => JSON.stringify(val, replacer, space);
15
+ return serializer(value);
16
+ function replacer(key, value) {
17
+ if (key !== '') {
18
+ path.push(key);
19
+ }
20
+ if (forbidReactElements && isReactElement(value)) {
21
+ throw new Error(genErrMsg('React element'));
22
+ }
23
+ if (isCallable(value)) {
24
+ const functionName = value.name;
25
+ throw new Error(genErrMsg('function', path.length === 0 ? functionName : undefined));
26
+ }
27
+ const valueOriginal = this[key];
28
+ for (const { is, serialize } of types.slice().reverse()) {
29
+ if (is(valueOriginal)) {
30
+ //@ts-ignore
31
+ return serialize(valueOriginal, serializer);
32
+ }
33
+ }
34
+ if (sortObjectKeys && isObject(value)) {
35
+ const copy = {};
36
+ Object.keys(value)
37
+ .sort()
38
+ .forEach((key) => {
39
+ copy[key] = value[key];
40
+ });
41
+ value = copy;
42
+ }
43
+ return value;
44
+ }
45
+ function genErrMsg(valueType, valName) {
46
+ const name = valName ? ' `' + valName + '`' : '';
47
+ const location = path.length === 0 ? '' : ` ${name ? 'at ' : ''}\`${valueName}[${path.map((p) => `'${p}'`).join('][')}]\``;
48
+ const fallback = name === '' && location === '' ? ` ${valueName}` : '';
49
+ return `Cannot serialize${name}${location}${fallback} because it is a ${valueType} (https://github.com/brillout/json-s)`;
50
+ }
51
+ }
@@ -0,0 +1,8 @@
1
+ export { types };
2
+ declare const types: readonly [Type<undefined, unknown>, Type<number, unknown>, Type<number, unknown>, Type<number, unknown>, Type<Date, any>, Type<BigInt, any>, Type<RegExp, any>, Type<Map<any, any>, any[]>, Type<Set<unknown>, unknown[]>, Type<string, any>];
3
+ declare type Type<T, IntermediateType> = {
4
+ is: (val: unknown) => asserts val is T;
5
+ match: (str: string) => boolean;
6
+ serialize: (val: T, serializer: (val: IntermediateType) => string) => string;
7
+ deserialize: (str: string, deserializer: (str: string) => IntermediateType) => T;
8
+ };
@@ -0,0 +1,80 @@
1
+ export { types };
2
+ const types = [
3
+ ts({
4
+ is: (val) => val === undefined,
5
+ match: (str) => str === '!undefined',
6
+ serialize: () => '!undefined',
7
+ deserialize: () => undefined
8
+ }),
9
+ ts({
10
+ is: (val) => val === Infinity,
11
+ match: (str) => str === '!Infinity',
12
+ serialize: () => '!Infinity',
13
+ deserialize: () => Infinity
14
+ }),
15
+ ts({
16
+ is: (val) => val === -Infinity,
17
+ match: (str) => str === '!-Infinity',
18
+ serialize: () => '!-Infinity',
19
+ deserialize: () => -Infinity
20
+ }),
21
+ ts({
22
+ is: (val) => typeof val === 'number' && isNaN(val),
23
+ match: (str) => str === '!NaN',
24
+ serialize: () => '!NaN',
25
+ deserialize: () => NaN
26
+ }),
27
+ ts({
28
+ is: (val) => val instanceof Date,
29
+ match: (str) => str.startsWith('!Date:'),
30
+ serialize: (val) => '!Date:' + val.toISOString(),
31
+ deserialize: (str) => new Date(str.slice('!Date:'.length))
32
+ }),
33
+ ts({
34
+ is: (val) => typeof val === 'bigint',
35
+ match: (str) => str.startsWith('!BigInt:'),
36
+ serialize: (val) => '!BigInt:' + val.toString(),
37
+ deserialize: (str) => {
38
+ if (typeof BigInt === 'undefined') {
39
+ throw new Error('Your JavaScript environement does not support BigInt. Consider adding a polyfill.');
40
+ }
41
+ return BigInt(str.slice('!BigInt:'.length));
42
+ }
43
+ }),
44
+ ts({
45
+ is: (val) => val instanceof RegExp,
46
+ match: (str) => str.startsWith('!RegExp:'),
47
+ serialize: (val) => '!RegExp:' + val.toString(),
48
+ deserialize: (str) => {
49
+ str = str.slice('!RegExp:'.length);
50
+ // const args: string[] = str.match(/\/(.*?)\/([gimy])?$/)!
51
+ const args = str.match(/\/(.*)\/(.*)?/);
52
+ const pattern = args[1];
53
+ const flags = args[2];
54
+ return new RegExp(pattern, flags);
55
+ }
56
+ }),
57
+ ts({
58
+ is: (val) => val instanceof Map,
59
+ match: (str) => str.startsWith('!Map:'),
60
+ serialize: (val, serializer) => '!Map:' + serializer(Array.from(val.entries())),
61
+ deserialize: (str, deserializer) => new Map(deserializer(str.slice('!Map:'.length)))
62
+ }),
63
+ ts({
64
+ is: (val) => val instanceof Set,
65
+ match: (str) => str.startsWith('!Set:'),
66
+ serialize: (val, serializer) => '!Set:' + serializer(Array.from(val.values())),
67
+ deserialize: (str, deserializer) => new Set(deserializer(str.slice('!Set:'.length)))
68
+ }),
69
+ // Avoid collisions with the special strings defined above
70
+ ts({
71
+ is: (val) => typeof val === 'string' && val.startsWith('!'),
72
+ match: (str) => str.startsWith('!'),
73
+ serialize: (val) => '!' + val,
74
+ deserialize: (str) => str.slice(1)
75
+ })
76
+ ];
77
+ // Type check
78
+ function ts(t) {
79
+ return t;
80
+ }
@@ -0,0 +1 @@
1
+ export declare function isCallable<T extends (...args: unknown[]) => unknown>(thing: T | unknown): thing is T;
@@ -0,0 +1,3 @@
1
+ export function isCallable(thing) {
2
+ return thing instanceof Function || typeof thing === 'function';
3
+ }
@@ -0,0 +1,3 @@
1
+ export { isObject };
2
+ declare type Object = Record<string, unknown>;
3
+ declare function isObject(value: unknown): value is Object;
@@ -0,0 +1,10 @@
1
+ export { isObject };
2
+ function isObject(value) {
3
+ if (typeof value !== 'object' || value === null) {
4
+ return false;
5
+ }
6
+ if (Array.isArray(value)) {
7
+ return false;
8
+ }
9
+ return true;
10
+ }
@@ -0,0 +1 @@
1
+ export declare function isReactElement(value: unknown): boolean;
@@ -0,0 +1,5 @@
1
+ export function isReactElement(value) {
2
+ return (typeof value === 'object' &&
3
+ value !== null &&
4
+ String(value['$$typeof']) === 'Symbol(react.element)');
5
+ }
package/index.js ADDED
@@ -0,0 +1 @@
1
+ throw new Error("Module `@brillout/json-serializer` doesn't exist anymore: load `@brillout/json-serializer/parse` and `@brillout/json-serializer/stringify` instead.")
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@brillout/json-serializer",
3
+ "version": "0.5.1",
4
+ "description": "Same as JSON but with added support for `Date`, `undefined`, `Map`, `Set`, and more.",
5
+ "main": "./index.js",
6
+ "exports": {
7
+ ".": "./index.js",
8
+ "./parse": {
9
+ "require": "./dist/cjs/parse.js",
10
+ "import": "./dist/esm/parse.js"
11
+ },
12
+ "./stringify": {
13
+ "require": "./dist/cjs/stringify.js",
14
+ "import": "./dist/esm/stringify.js"
15
+ }
16
+ },
17
+ "scripts": {
18
+ "dev": "pnpm run tsc:watch:cjs",
19
+ "build": "pnpm run clean && pnpm run tsc:esm && pnpm run tsc:cjs",
20
+ "test": "self-import && node test/",
21
+ "docs": "mdocs",
22
+ "tsc:esm": "tsc",
23
+ "tsc:cjs": "tsc --project ./tsconfig.cjs.json",
24
+ "tsc:watch:esm": "tsc --incremental --watch",
25
+ "tsc:watch:cjs": "tsc --incremental --watch --project ./tsconfig.cjs.json",
26
+ "clean": "rm -rf dist/",
27
+ "reset": "git clean -Xdf && pnpm install && pnpm build",
28
+ "release": "release-me patch"
29
+ },
30
+ "devDependencies": {
31
+ "@brillout/mdocs": "^0.1.30",
32
+ "@brillout/release-me": "^0.0.5",
33
+ "@types/node": "17.0.13",
34
+ "lodash.isequal": "^4.5.0",
35
+ "react": "^17.0.2",
36
+ "self-import": "^1.0.1",
37
+ "typescript": "^4.8.2"
38
+ },
39
+ "files": [
40
+ "dist/",
41
+ "*.d.ts",
42
+ "*.js"
43
+ ],
44
+ "repository": "github:brillout/json-serializer",
45
+ "packageManager": "pnpm@7.9.5",
46
+ "publishConfig": {
47
+ "access": "public"
48
+ }
49
+ }
package/parse.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ // Help TS's resolver until it supports `package.json#exports`
2
+ export { parse } from './dist/esm/parse'
package/parse.js ADDED
@@ -0,0 +1,8 @@
1
+ // Some tools don't support `package.json#exports`, such as:
2
+ // - Nuxt v2
3
+ // - Expo/Metro
4
+ // - ESLint
5
+ // prettier-ignore
6
+ 'use strict';
7
+ // prettier-ignore
8
+ exports.parse = require('./dist/cjs/parse.js').parse;
package/readme.md ADDED
@@ -0,0 +1,325 @@
1
+ <!---
2
+
3
+
4
+
5
+
6
+
7
+
8
+ WARNING, READ THIS.
9
+ This is a computed file. Do not edit.
10
+ Instead, edit `/readme.template.md` and run `npm run docs` (or `yarn docs`).
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+ WARNING, READ THIS.
24
+ This is a computed file. Do not edit.
25
+ Instead, edit `/readme.template.md` and run `npm run docs` (or `yarn docs`).
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+ WARNING, READ THIS.
39
+ This is a computed file. Do not edit.
40
+ Instead, edit `/readme.template.md` and run `npm run docs` (or `yarn docs`).
41
+
42
+
43
+
44
+
45
+
46
+
47
+
48
+
49
+
50
+
51
+
52
+
53
+ WARNING, READ THIS.
54
+ This is a computed file. Do not edit.
55
+ Instead, edit `/readme.template.md` and run `npm run docs` (or `yarn docs`).
56
+
57
+
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+
68
+ WARNING, READ THIS.
69
+ This is a computed file. Do not edit.
70
+ Instead, edit `/readme.template.md` and run `npm run docs` (or `yarn docs`).
71
+
72
+
73
+
74
+
75
+
76
+
77
+ -->
78
+ # `@brillout/json-serializer`
79
+
80
+ Same as JSON but with added support for:
81
+ - `Date`
82
+ - `undefined`
83
+ - `Set`
84
+ - `Map`
85
+ - `BigInt`
86
+ - `RegExp`
87
+ - `NaN`
88
+ - `Infinity`
89
+
90
+ JSON is a good serializer for JavaScript values but
91
+ is lacking some JavaScript types such as `Date`:
92
+
93
+ ~~~js
94
+ const assert = require('assert')
95
+
96
+ let obj = {
97
+ time: new Date('2042-01-01')
98
+ }
99
+
100
+ // JSON converts dates to strings
101
+ assert(obj.time.constructor === Date)
102
+ obj = JSON.parse(JSON.stringify(obj))
103
+ assert(obj.time.constructor === String)
104
+ assert(obj.time === '2042-01-01T00:00:00.000Z')
105
+ ~~~
106
+
107
+ Whereas with `@brillout/json-serializer`:
108
+
109
+ ~~~js
110
+ const assert = require('assert')
111
+ const { parse } = require('@brillout/json-serializer/parse')
112
+ const { stringify } = require('@brillout/json-serializer/stringify')
113
+
114
+ let obj = {
115
+ time: new Date('2042-01-01')
116
+ }
117
+
118
+ // `@brillout/json-serializer` preserves Date
119
+ assert(obj.time.constructor === Date)
120
+ obj = parse(stringify(obj))
121
+ assert(obj.time.constructor === Date)
122
+ assert(obj.time.getTime() === new Date('2042-01-01').getTime())
123
+ ~~~
124
+
125
+ <br/>
126
+
127
+ #### Contents
128
+
129
+ - [Usage](#usage)
130
+ - [Full Example](#full-example)
131
+ - [How it Works](#how-it-works)
132
+
133
+
134
+ <br/>
135
+
136
+ ### Usage
137
+
138
+ ~~~js
139
+ // npm install @brillout/json-serializer
140
+ const { parse } = require('@brillout/json-serializer/parse')
141
+ const { stringify } = require('@brillout/json-serializer/stringify')
142
+
143
+ const obj = {
144
+ hello: 'from the future',
145
+ time: new Date('2042-01-01')
146
+ }
147
+
148
+ // Serialize with @brillout/json-serializer
149
+ const obj_serialized = stringify(obj)
150
+
151
+ // Deserialize a @brillout/json-serializer string
152
+ const obj_deserialized = parse(obj_serialized)
153
+ ~~~
154
+
155
+ <br/>
156
+
157
+ ### Full Example
158
+
159
+ Example exposing all differences between JSON and `@brillout/json-serializer`.
160
+
161
+ ~~~js
162
+ // /examples/json-serializer.js
163
+
164
+ const assert = require('assert')
165
+
166
+ const { parse } = require('@brillout/json-serializer/parse')
167
+ const { stringify } = require('@brillout/json-serializer/stringify')
168
+
169
+ const obj = {
170
+ date: new Date(),
171
+ undefined: undefined,
172
+ NaN: NaN,
173
+ Infinity: Infinity,
174
+ regexp: /^\d+$/g
175
+ }
176
+
177
+ // All of `obj` can be serialized with @brillout/json-serializer
178
+ const obj2 = parse(stringify(obj))
179
+ assert(obj2.date.getTime() === obj.date.getTime())
180
+ assert(obj2.undefined === undefined && 'undefined' in obj2)
181
+ assert(isNaN(obj2.NaN))
182
+ assert(obj2.Infinity === Infinity)
183
+ assert(obj2.regexp.toString() === obj.regexp.toString())
184
+
185
+ // JSON cannot serialize any of `obj`
186
+ const obj3 = JSON.parse(JSON.stringify(obj))
187
+ // JSON converts dates to strings
188
+ assert(obj3.constructor !== Date)
189
+ // JSON removes properties with a value of `undefined`
190
+ assert(!('undefined' in obj3))
191
+ // JSON converts `NaN` to `null`
192
+ assert(obj3.NaN === null)
193
+ // JSON converts `Infinity` to `null`
194
+ assert(obj3.Infinity === null)
195
+ // JSON converts RegExp to an empty object
196
+ assert(obj3.regexp.constructor === Object && Object.keys(obj3.regexp).length === 0)
197
+ ~~~
198
+
199
+ To run the example:
200
+
201
+ ~~~shell
202
+ $ git clone git@github.com:brillout/json-serializer
203
+ $ cd json-serializer
204
+ $ npm install
205
+ $ npm run self-link
206
+ $ node ./examples/json-serializer.js
207
+ ~~~
208
+
209
+ The `npm run self-link` is required to be able to self `require('@brillout/json-serializer')`.
210
+
211
+ <br/>
212
+
213
+ ### How it Works
214
+
215
+ Let's see how `@brillout/json-serializer` serializes an object:
216
+
217
+ ~~~js
218
+ // /examples/inspect.js
219
+
220
+ const { stringify } = require('@brillout/json-serializer/stringify')
221
+
222
+ const obj = {
223
+ date: new Date(),
224
+ undefined: undefined,
225
+ collision: '!undefined',
226
+ NaN: NaN,
227
+ Infinity: Infinity,
228
+ regexp: /^\d+$/g
229
+ }
230
+
231
+ console.log(stringify(obj, undefined, 2))
232
+ // Prints:
233
+ /*
234
+ {
235
+ "date": "!Date:2021-01-12T22:15:56.319Z",
236
+ "undefined": "!undefined",
237
+ "collision": "!!undefined"
238
+ "NaN": "!NaN",
239
+ "Infinity": "!Infinity",
240
+ "regexp": "!RegExp:/^\\d+$/g"
241
+ }
242
+ */
243
+ ~~~
244
+
245
+ `@brillout/json-serializer` is based on JSON while using prefixed strings for unsupported types.
246
+
247
+ `@brillout/json-serializer` uses the native `JSON.parse` and `JSON.stringify` functions while modifying the serialization of unsupported types.
248
+
249
+ <!---
250
+
251
+
252
+
253
+
254
+
255
+
256
+ WARNING, READ THIS.
257
+ This is a computed file. Do not edit.
258
+ Instead, edit `/readme.template.md` and run `npm run docs` (or `yarn docs`).
259
+
260
+
261
+
262
+
263
+
264
+
265
+
266
+
267
+
268
+
269
+
270
+
271
+ WARNING, READ THIS.
272
+ This is a computed file. Do not edit.
273
+ Instead, edit `/readme.template.md` and run `npm run docs` (or `yarn docs`).
274
+
275
+
276
+
277
+
278
+
279
+
280
+
281
+
282
+
283
+
284
+
285
+
286
+ WARNING, READ THIS.
287
+ This is a computed file. Do not edit.
288
+ Instead, edit `/readme.template.md` and run `npm run docs` (or `yarn docs`).
289
+
290
+
291
+
292
+
293
+
294
+
295
+
296
+
297
+
298
+
299
+
300
+
301
+ WARNING, READ THIS.
302
+ This is a computed file. Do not edit.
303
+ Instead, edit `/readme.template.md` and run `npm run docs` (or `yarn docs`).
304
+
305
+
306
+
307
+
308
+
309
+
310
+
311
+
312
+
313
+
314
+
315
+
316
+ WARNING, READ THIS.
317
+ This is a computed file. Do not edit.
318
+ Instead, edit `/readme.template.md` and run `npm run docs` (or `yarn docs`).
319
+
320
+
321
+
322
+
323
+
324
+
325
+ -->
@@ -0,0 +1,77 @@
1
+ # `@brillout/json-serializer`
2
+
3
+ Same as JSON but with added support for:
4
+ - `Date`
5
+ - `undefined`
6
+ - `Set`
7
+ - `Map`
8
+ - `BigInt`
9
+ - `RegExp`
10
+ - `NaN`
11
+ - `Infinity`
12
+
13
+ JSON is a good serializer for JavaScript values but
14
+ is lacking some JavaScript types such as `Date`:
15
+
16
+ ~~~js
17
+ !INLINE /examples/date_json.js --hide-source-path
18
+ ~~~
19
+
20
+ Whereas with `@brillout/json-serializer`:
21
+
22
+ ~~~js
23
+ !INLINE /examples/date_json-serializer.js --hide-source-path
24
+ ~~~
25
+
26
+ <br/>
27
+
28
+ #### Contents
29
+
30
+ - [Usage](#usage)
31
+ - [Full Example](#full-example)
32
+ - [How it Works](#how-it-works)
33
+
34
+
35
+ <br/>
36
+
37
+ ### Usage
38
+
39
+ ~~~js
40
+ !INLINE /examples/simple.js --hide-source-path
41
+ ~~~
42
+
43
+ <br/>
44
+
45
+ ### Full Example
46
+
47
+ Example exposing all differences between JSON and `@brillout/json-serializer`.
48
+
49
+ ~~~js
50
+ !INLINE /examples/json-serializer.js
51
+ ~~~
52
+
53
+ To run the example:
54
+
55
+ ~~~shell
56
+ $ git clone git@github.com:brillout/json-serializer
57
+ $ cd json-serializer
58
+ $ npm install
59
+ $ npm run self-link
60
+ $ node ./examples/json-serializer.js
61
+ ~~~
62
+
63
+ The `npm run self-link` is required to be able to self `require('@brillout/json-serializer')`.
64
+
65
+ <br/>
66
+
67
+ ### How it Works
68
+
69
+ Let's see how `@brillout/json-serializer` serializes an object:
70
+
71
+ ~~~js
72
+ !INLINE /examples/inspect.js
73
+ ~~~
74
+
75
+ `@brillout/json-serializer` is based on JSON while using prefixed strings for unsupported types.
76
+
77
+ `@brillout/json-serializer` uses the native `JSON.parse` and `JSON.stringify` functions while modifying the serialization of unsupported types.
package/stringify.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ // Help TS's resolver until it supports `package.json#exports`
2
+ export { stringify } from './dist/esm/stringify'
package/stringify.js ADDED
@@ -0,0 +1,8 @@
1
+ // Some tools don't support `package.json#exports`, such as:
2
+ // - Nuxt v2
3
+ // - Expo/Metro
4
+ // - ESLint
5
+ // prettier-ignore
6
+ 'use strict';
7
+ // prettier-ignore
8
+ exports.stringify = require('./dist/cjs/stringify.js').stringify;