@brillout/json-serializer 0.5.5 → 0.5.7

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.
@@ -1,7 +1,12 @@
1
1
  export { stringify };
2
+ export { isJsonSerializerError };
2
3
  declare function stringify(value: unknown, { forbidReactElements, space, valueName, sortObjectKeys }?: {
3
4
  forbidReactElements?: boolean;
4
5
  space?: number;
5
6
  valueName?: string;
6
7
  sortObjectKeys?: boolean;
7
8
  }): string;
9
+ type JsonSerializerError = Error & {
10
+ messageCore: string;
11
+ };
12
+ declare function isJsonSerializerError(thing: unknown): thing is JsonSerializerError;
@@ -1,30 +1,27 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.stringify = void 0;
3
+ exports.isJsonSerializerError = exports.stringify = void 0;
4
4
  const types_1 = require("./types");
5
5
  const isReactElement_1 = require("./utils/isReactElement");
6
6
  const isCallable_1 = require("./utils/isCallable");
7
7
  const isObject_1 = require("./utils/isObject");
8
- function stringify(value, { forbidReactElements, space, valueName = 'value', sortObjectKeys } = {}) {
9
- const path = [];
8
+ const replacerWithPath_1 = require("./utils/replacerWithPath");
9
+ function stringify(value, { forbidReactElements, space, valueName, sortObjectKeys } = {}) {
10
10
  // The only error `JSON.stringify()` can throw is `TypeError "cyclic object value"`.
11
11
  // - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#exceptions
12
12
  // - This means we have total of 3 possible errors while serializing:
13
13
  // - Cyclic references
14
14
  // - Functions
15
15
  // - React elements
16
- const serializer = (val) => JSON.stringify(val, replacer, space);
16
+ const serializer = (val) => JSON.stringify(val, (0, replacerWithPath_1.replacerWithPath)(replacer, !valueName), space);
17
17
  return serializer(value);
18
- function replacer(key, value) {
19
- if (key !== '') {
20
- path.push(key);
21
- }
18
+ function replacer(key, value, path) {
22
19
  if (forbidReactElements && (0, isReactElement_1.isReactElement)(value)) {
23
- throw new Error(genErrMsg('React element'));
20
+ throw genErr(genErrMsg('React element', path, valueName));
24
21
  }
25
22
  if ((0, isCallable_1.isCallable)(value)) {
26
23
  const functionName = value.name;
27
- throw new Error(genErrMsg('function', path.length === 0 ? functionName : undefined));
24
+ throw genErr(genErrMsg('function', path, valueName, path.length === 0 ? functionName : undefined));
28
25
  }
29
26
  const valueOriginal = this[key];
30
27
  for (const { is, serialize } of types_1.types.slice().reverse()) {
@@ -44,11 +41,34 @@ function stringify(value, { forbidReactElements, space, valueName = 'value', sor
44
41
  }
45
42
  return value;
46
43
  }
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 `@brillout/json-serializer (https://github.com/brillout/json-serializer) cannot serialize${name}${location}${fallback} because it's a ${valueType}.`;
52
- }
53
44
  }
54
45
  exports.stringify = stringify;
46
+ const stamp = '_isJsonSerializerError';
47
+ function genErr(errMsg) {
48
+ const err = new Error(`[@brillout/json-serializer](https://github.com/brillout/json-serializer) ${errMsg}.`);
49
+ Object.assign(err, {
50
+ messageCore: errMsg,
51
+ [stamp]: true
52
+ });
53
+ return err;
54
+ }
55
+ function isJsonSerializerError(thing) {
56
+ return (0, isObject_1.isObject)(thing) && thing[stamp] === true;
57
+ }
58
+ exports.isJsonSerializerError = isJsonSerializerError;
59
+ function genErrMsg(valueType, path, rootValueName, problematicValueName) {
60
+ let subject;
61
+ if (!path) {
62
+ subject = rootValueName || problematicValueName || 'value';
63
+ }
64
+ else {
65
+ if (problematicValueName) {
66
+ subject = problematicValueName + ' at ';
67
+ }
68
+ else {
69
+ subject = '';
70
+ }
71
+ subject = subject + (rootValueName || '') + path;
72
+ }
73
+ return `cannot serialize ${subject} because it's a ${valueType}`;
74
+ }
@@ -1,6 +1,6 @@
1
1
  export { types };
2
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> = {
3
+ type Type<T, IntermediateType> = {
4
4
  is: (val: unknown) => asserts val is T;
5
5
  match: (str: string) => boolean;
6
6
  serialize: (val: T, serializer: (val: IntermediateType) => string) => string;
@@ -0,0 +1 @@
1
+ export declare function isKeyDotNotationCompatible(key: string): boolean;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isKeyDotNotationCompatible = void 0;
4
+ function isKeyDotNotationCompatible(key) {
5
+ return /^[a-z0-9\$_]+$/i.test(key);
6
+ }
7
+ exports.isKeyDotNotationCompatible = isKeyDotNotationCompatible;
@@ -1,3 +1,3 @@
1
1
  export { isObject };
2
- declare type Object = Record<string, unknown>;
2
+ type Object = Record<string, unknown>;
3
3
  declare function isObject(value: unknown): value is Object;
@@ -0,0 +1,3 @@
1
+ export { replacerWithPath };
2
+ type Iterable = Record<string, unknown>;
3
+ declare function replacerWithPath(replacer: (this: Iterable, key: string, value: unknown, path: string) => unknown, canBeFirstKey: boolean): (this: Iterable, key: string, value: unknown) => unknown;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.replacerWithPath = void 0;
4
+ // https://stackoverflow.com/questions/61681176/json-stringify-replacer-how-to-get-full-path/63957172#63957172
5
+ const isKeyDotNotationCompatible_1 = require("./isKeyDotNotationCompatible");
6
+ function replacerWithPath(replacer, canBeFirstKey) {
7
+ const paths = new WeakMap();
8
+ return function (key, value) {
9
+ const prefix = paths.get(this);
10
+ const path = (prefix !== null && prefix !== void 0 ? prefix : '') + (key ? getPropAccessNotation(key, this, !prefix && canBeFirstKey) : '');
11
+ if (isIterable(value))
12
+ paths.set(value, path);
13
+ return replacer.call(this, key, value, path);
14
+ };
15
+ }
16
+ exports.replacerWithPath = replacerWithPath;
17
+ function isIterable(value) {
18
+ return value === Object(value);
19
+ }
20
+ function getPropAccessNotation(key, obj, isFirstKey) {
21
+ if (Array.isArray(obj))
22
+ return `[${key}]`;
23
+ if ((0, isKeyDotNotationCompatible_1.isKeyDotNotationCompatible)(key))
24
+ return !isFirstKey ? `.${key}` : key; // Dot notation
25
+ return `[${JSON.stringify(key)}]`; // Bracket notation
26
+ }
@@ -1,7 +1,12 @@
1
1
  export { stringify };
2
+ export { isJsonSerializerError };
2
3
  declare function stringify(value: unknown, { forbidReactElements, space, valueName, sortObjectKeys }?: {
3
4
  forbidReactElements?: boolean;
4
5
  space?: number;
5
6
  valueName?: string;
6
7
  sortObjectKeys?: boolean;
7
8
  }): string;
9
+ type JsonSerializerError = Error & {
10
+ messageCore: string;
11
+ };
12
+ declare function isJsonSerializerError(thing: unknown): thing is JsonSerializerError;
@@ -1,28 +1,26 @@
1
1
  export { stringify };
2
+ export { isJsonSerializerError };
2
3
  import { types } from './types';
3
4
  import { isReactElement } from './utils/isReactElement';
4
5
  import { isCallable } from './utils/isCallable';
5
6
  import { isObject } from './utils/isObject';
6
- function stringify(value, { forbidReactElements, space, valueName = 'value', sortObjectKeys } = {}) {
7
- const path = [];
7
+ import { replacerWithPath } from './utils/replacerWithPath';
8
+ function stringify(value, { forbidReactElements, space, valueName, sortObjectKeys } = {}) {
8
9
  // The only error `JSON.stringify()` can throw is `TypeError "cyclic object value"`.
9
10
  // - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#exceptions
10
11
  // - This means we have total of 3 possible errors while serializing:
11
12
  // - Cyclic references
12
13
  // - Functions
13
14
  // - React elements
14
- const serializer = (val) => JSON.stringify(val, replacer, space);
15
+ const serializer = (val) => JSON.stringify(val, replacerWithPath(replacer, !valueName), space);
15
16
  return serializer(value);
16
- function replacer(key, value) {
17
- if (key !== '') {
18
- path.push(key);
19
- }
17
+ function replacer(key, value, path) {
20
18
  if (forbidReactElements && isReactElement(value)) {
21
- throw new Error(genErrMsg('React element'));
19
+ throw genErr(genErrMsg('React element', path, valueName));
22
20
  }
23
21
  if (isCallable(value)) {
24
22
  const functionName = value.name;
25
- throw new Error(genErrMsg('function', path.length === 0 ? functionName : undefined));
23
+ throw genErr(genErrMsg('function', path, valueName, path.length === 0 ? functionName : undefined));
26
24
  }
27
25
  const valueOriginal = this[key];
28
26
  for (const { is, serialize } of types.slice().reverse()) {
@@ -42,10 +40,32 @@ function stringify(value, { forbidReactElements, space, valueName = 'value', sor
42
40
  }
43
41
  return value;
44
42
  }
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 `@brillout/json-serializer (https://github.com/brillout/json-serializer) cannot serialize${name}${location}${fallback} because it's a ${valueType}.`;
43
+ }
44
+ const stamp = '_isJsonSerializerError';
45
+ function genErr(errMsg) {
46
+ const err = new Error(`[@brillout/json-serializer](https://github.com/brillout/json-serializer) ${errMsg}.`);
47
+ Object.assign(err, {
48
+ messageCore: errMsg,
49
+ [stamp]: true
50
+ });
51
+ return err;
52
+ }
53
+ function isJsonSerializerError(thing) {
54
+ return isObject(thing) && thing[stamp] === true;
55
+ }
56
+ function genErrMsg(valueType, path, rootValueName, problematicValueName) {
57
+ let subject;
58
+ if (!path) {
59
+ subject = rootValueName || problematicValueName || 'value';
60
+ }
61
+ else {
62
+ if (problematicValueName) {
63
+ subject = problematicValueName + ' at ';
64
+ }
65
+ else {
66
+ subject = '';
67
+ }
68
+ subject = subject + (rootValueName || '') + path;
50
69
  }
70
+ return `cannot serialize ${subject} because it's a ${valueType}`;
51
71
  }
@@ -1,6 +1,6 @@
1
1
  export { types };
2
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> = {
3
+ type Type<T, IntermediateType> = {
4
4
  is: (val: unknown) => asserts val is T;
5
5
  match: (str: string) => boolean;
6
6
  serialize: (val: T, serializer: (val: IntermediateType) => string) => string;
@@ -0,0 +1 @@
1
+ export declare function isKeyDotNotationCompatible(key: string): boolean;
@@ -0,0 +1,3 @@
1
+ export function isKeyDotNotationCompatible(key) {
2
+ return /^[a-z0-9\$_]+$/i.test(key);
3
+ }
@@ -1,3 +1,3 @@
1
1
  export { isObject };
2
- declare type Object = Record<string, unknown>;
2
+ type Object = Record<string, unknown>;
3
3
  declare function isObject(value: unknown): value is Object;
@@ -0,0 +1,3 @@
1
+ export { replacerWithPath };
2
+ type Iterable = Record<string, unknown>;
3
+ declare function replacerWithPath(replacer: (this: Iterable, key: string, value: unknown, path: string) => unknown, canBeFirstKey: boolean): (this: Iterable, key: string, value: unknown) => unknown;
@@ -0,0 +1,23 @@
1
+ export { replacerWithPath };
2
+ // https://stackoverflow.com/questions/61681176/json-stringify-replacer-how-to-get-full-path/63957172#63957172
3
+ import { isKeyDotNotationCompatible } from './isKeyDotNotationCompatible';
4
+ function replacerWithPath(replacer, canBeFirstKey) {
5
+ const paths = new WeakMap();
6
+ return function (key, value) {
7
+ const prefix = paths.get(this);
8
+ const path = (prefix ?? '') + (key ? getPropAccessNotation(key, this, !prefix && canBeFirstKey) : '');
9
+ if (isIterable(value))
10
+ paths.set(value, path);
11
+ return replacer.call(this, key, value, path);
12
+ };
13
+ }
14
+ function isIterable(value) {
15
+ return value === Object(value);
16
+ }
17
+ function getPropAccessNotation(key, obj, isFirstKey) {
18
+ if (Array.isArray(obj))
19
+ return `[${key}]`;
20
+ if (isKeyDotNotationCompatible(key))
21
+ return !isFirstKey ? `.${key}` : key; // Dot notation
22
+ return `[${JSON.stringify(key)}]`; // Bracket notation
23
+ }
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@brillout/json-serializer",
3
- "version": "0.5.5",
3
+ "version": "0.5.7",
4
+ "dependencies": {},
4
5
  "description": "Same as JSON but with added support for `Date`, `undefined`, `Map`, `Set`, and more.",
5
6
  "main": "./index.mjs",
6
- "license": "MIT",
7
7
  "exports": {
8
8
  ".": "./index.mjs",
9
9
  "./parse": {
@@ -20,7 +20,7 @@
20
20
  "scripts": {
21
21
  "dev": "pnpm run tsc:watch:cjs",
22
22
  "build": "pnpm run clean && pnpm run tsc:esm && pnpm run tsc:cjs",
23
- "test": "self-import && node test/",
23
+ "test": "vitest",
24
24
  "docs": "mdocs",
25
25
  "tsc:esm": "tsc",
26
26
  "tsc:cjs": "tsc --project ./tsconfig.cjs.json",
@@ -33,11 +33,19 @@
33
33
  "devDependencies": {
34
34
  "@brillout/mdocs": "^0.1.30",
35
35
  "@brillout/release-me": "^0.0.5",
36
- "@types/node": "17.0.13",
36
+ "@types/node": "^20.5.6",
37
+ "@types/react": "^18.2.21",
37
38
  "lodash.isequal": "^4.5.0",
38
39
  "react": "^17.0.2",
39
- "self-import": "^1.0.1",
40
- "typescript": "^4.8.2"
40
+ "typescript": "^5.2.2",
41
+ "vitest": "^0.34.3"
42
+ },
43
+ "packageManager": "pnpm@8.6.12",
44
+ "// Use @vitest/snapshot PR https://github.com/vitest-dev/vitest/pull/3961": "",
45
+ "pnpm": {
46
+ "overrides": {
47
+ "vitest>@vitest/snapshot": "npm:@brillout/vitest-snapshot@0.35.0-prerelease"
48
+ }
41
49
  },
42
50
  "files": [
43
51
  "dist/",
@@ -46,7 +54,7 @@
46
54
  "*.js"
47
55
  ],
48
56
  "repository": "github:brillout/json-serializer",
49
- "packageManager": "pnpm@7.9.5",
57
+ "license": "MIT",
50
58
  "publishConfig": {
51
59
  "access": "public"
52
60
  }