@brillout/json-serializer 0.5.10 → 0.5.11

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,16 +1,19 @@
1
1
  export { stringify };
2
2
  export { isJsonSerializerError };
3
- import { type Iterable } from './utils/replacerWithPath';
3
+ import { type Iterable, type Path } from './utils/replacerWithPath';
4
4
  declare function stringify(value: unknown, { forbidReactElements, space, valueName, sortObjectKeys, replacer: replacerUserProvided, }?: {
5
5
  forbidReactElements?: boolean;
6
6
  space?: number;
7
7
  valueName?: string;
8
8
  sortObjectKeys?: boolean;
9
- replacer?: (this: Iterable, key: string, value: unknown, path: string) => void | {
9
+ replacer?: (this: Iterable, key: string, value: unknown) => void | {
10
10
  replacement: unknown;
11
11
  };
12
12
  }): string;
13
- type JsonSerializerError = Error & {
13
+ type ErrAddendum = {
14
14
  messageCore: string;
15
+ value: unknown;
16
+ path: Path;
17
+ pathString: string;
15
18
  };
16
- declare function isJsonSerializerError(thing: unknown): thing is JsonSerializerError;
19
+ declare function isJsonSerializerError(thing: unknown): thing is Error & ErrAddendum;
@@ -6,27 +6,43 @@ const isReactElement_1 = require("./utils/isReactElement");
6
6
  const isCallable_1 = require("./utils/isCallable");
7
7
  const isObject_1 = require("./utils/isObject");
8
8
  const replacerWithPath_1 = require("./utils/replacerWithPath");
9
- function stringify(value, { forbidReactElements, space, valueName, sortObjectKeys, replacer: replacerUserProvided, } = {}) {
9
+ function stringify(value, { forbidReactElements, space, valueName, sortObjectKeys,
10
+ // Used by Vike: https://github.com/vikejs/vike/blob/b4ba6b70e6bdc2e1f460c0d2e4c3faae5d0a733c/vike/node/plugin/plugins/importUserCode/v1-design/getConfigValuesSerialized.ts#L78
11
+ replacer: replacerUserProvided, } = {}) {
12
+ const canBeFirstKey = !valueName;
10
13
  // The only error `JSON.stringify()` can throw is `TypeError "cyclic object value"`.
11
14
  // - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#exceptions
12
15
  // - This means we have total of 3 possible errors while serializing:
13
16
  // - Cyclic references
14
17
  // - Functions
15
18
  // - React elements
16
- const serializer = (val) => JSON.stringify(val, (0, replacerWithPath_1.replacerWithPath)(replacer, !valueName), space);
19
+ const serializer = (val) => JSON.stringify(val, (0, replacerWithPath_1.replacerWithPath)(replacer), space);
17
20
  return serializer(value);
18
21
  function replacer(key, value, path) {
19
22
  {
20
- const ret = replacerUserProvided === null || replacerUserProvided === void 0 ? void 0 : replacerUserProvided.call(this, key, value, path);
23
+ const ret = replacerUserProvided === null || replacerUserProvided === void 0 ? void 0 : replacerUserProvided.call(this, key, value);
21
24
  if (ret)
22
25
  return ret.replacement;
23
26
  }
24
27
  if (forbidReactElements && (0, isReactElement_1.isReactElement)(value)) {
25
- throw genErr(genErrMsg('React element', path, valueName));
28
+ throw genErr({
29
+ value,
30
+ valueType: 'React element',
31
+ path,
32
+ canBeFirstKey,
33
+ rootValueName: valueName,
34
+ });
26
35
  }
27
36
  if ((0, isCallable_1.isCallable)(value)) {
28
37
  const functionName = value.name;
29
- throw genErr(genErrMsg('function', path, valueName, path.length === 0 ? functionName : undefined));
38
+ throw genErr({
39
+ value,
40
+ valueType: 'function',
41
+ path,
42
+ canBeFirstKey,
43
+ rootValueName: valueName,
44
+ problematicValueName: path.length === 0 ? functionName : undefined,
45
+ });
30
46
  }
31
47
  const valueOriginal = this[key];
32
48
  for (const { is, serialize } of types_1.types.slice().reverse()) {
@@ -48,22 +64,28 @@ function stringify(value, { forbidReactElements, space, valueName, sortObjectKey
48
64
  }
49
65
  }
50
66
  exports.stringify = stringify;
51
- const stamp = '_isJsonSerializerError';
52
- function genErr(errMsg) {
67
+ function genErr({ value, valueType, path, canBeFirstKey, rootValueName, problematicValueName, }) {
68
+ const pathString = getPathString(path, canBeFirstKey);
69
+ const errMsg = getErrMsg({ valueType, pathString, rootValueName, problematicValueName });
53
70
  const err = new Error(`[@brillout/json-serializer](https://github.com/brillout/json-serializer) ${errMsg}.`);
54
- Object.assign(err, {
71
+ const errAddendum = {
55
72
  messageCore: errMsg,
56
73
  [stamp]: true,
57
- });
74
+ value,
75
+ path,
76
+ pathString,
77
+ };
78
+ Object.assign(err, errAddendum);
58
79
  return err;
59
80
  }
81
+ const stamp = '_isJsonSerializerError';
60
82
  function isJsonSerializerError(thing) {
61
83
  return (0, isObject_1.isObject)(thing) && thing[stamp] === true;
62
84
  }
63
85
  exports.isJsonSerializerError = isJsonSerializerError;
64
- function genErrMsg(valueType, path, rootValueName, problematicValueName) {
86
+ function getErrMsg({ valueType, rootValueName, pathString, problematicValueName, }) {
65
87
  let subject;
66
- if (!path) {
88
+ if (!pathString) {
67
89
  subject = rootValueName || problematicValueName || 'value';
68
90
  }
69
91
  else {
@@ -73,7 +95,27 @@ function genErrMsg(valueType, path, rootValueName, problematicValueName) {
73
95
  else {
74
96
  subject = '';
75
97
  }
76
- subject = subject + (rootValueName || '') + path;
98
+ subject = subject + (rootValueName || '') + pathString;
77
99
  }
78
100
  return `cannot serialize ${subject} because it's a ${valueType}`;
79
101
  }
102
+ function getPathString(path, canBeFirstKey) {
103
+ const pathString = path
104
+ .map((key, i) => {
105
+ if (typeof key === 'number') {
106
+ return `[${key}]`;
107
+ }
108
+ if (i === 0 && canBeFirstKey && isKeyDotNotationCompatible(key)) {
109
+ return key;
110
+ }
111
+ return getPropAccessNotation(key);
112
+ })
113
+ .join('');
114
+ return pathString;
115
+ }
116
+ function getPropAccessNotation(key) {
117
+ return typeof key === 'string' && isKeyDotNotationCompatible(key) ? `.${key}` : `[${JSON.stringify(key)}]`;
118
+ }
119
+ function isKeyDotNotationCompatible(key) {
120
+ return /^[a-z0-9\$_]+$/i.test(key);
121
+ }
@@ -1 +0,0 @@
1
- export declare function isKeyDotNotationCompatible(key: string): boolean;
@@ -1,7 +1 @@
1
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,5 +1,7 @@
1
1
  export { replacerWithPath };
2
2
  export type { Iterable };
3
+ export type { Path };
4
+ type Path = (string | number)[];
3
5
  type Iterable = Record<string, unknown>;
4
- type Replacer = (this: Iterable, key: string, value: unknown, path: string) => unknown;
5
- declare function replacerWithPath(replacer: Replacer, canBeFirstKey: boolean): (this: Iterable, key: string, value: unknown) => unknown;
6
+ type Replacer = (this: Iterable, key: string, value: unknown, path: Path) => unknown;
7
+ declare function replacerWithPath(replacer: Replacer): (this: Iterable, key: string, value: unknown) => unknown;
@@ -1,15 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
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();
4
+ function replacerWithPath(replacer) {
5
+ const pathMap = new WeakMap();
8
6
  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) : '');
7
+ var _a;
8
+ const pathPrevious = (_a = pathMap.get(this)) !== null && _a !== void 0 ? _a : [];
9
+ const path = [...pathPrevious];
10
+ if (key !== '') {
11
+ const pathEntry = !Array.isArray(this) ? key : parseInt(key, 10);
12
+ path.push(pathEntry);
13
+ }
11
14
  if (isIterable(value))
12
- paths.set(value, path);
15
+ pathMap.set(value, path);
13
16
  return replacer.call(this, key, value, path);
14
17
  };
15
18
  }
@@ -17,10 +20,3 @@ exports.replacerWithPath = replacerWithPath;
17
20
  function isIterable(value) {
18
21
  return value === Object(value);
19
22
  }
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
- }
package/dist/esm/parse.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { parse };
2
+ // Used by Vike: https://github.com/vikejs/vike/blob/b4ba6b70e6bdc2e1f460c0d2e4c3faae5d0a733c/vike/shared/page-configs/serialize/parseConfigValuesSerialized.ts#L13
2
3
  export { parseTransform };
3
4
  import { types } from './types';
4
5
  function parse(str) {
@@ -1,16 +1,19 @@
1
1
  export { stringify };
2
2
  export { isJsonSerializerError };
3
- import { type Iterable } from './utils/replacerWithPath';
3
+ import { type Iterable, type Path } from './utils/replacerWithPath';
4
4
  declare function stringify(value: unknown, { forbidReactElements, space, valueName, sortObjectKeys, replacer: replacerUserProvided, }?: {
5
5
  forbidReactElements?: boolean;
6
6
  space?: number;
7
7
  valueName?: string;
8
8
  sortObjectKeys?: boolean;
9
- replacer?: (this: Iterable, key: string, value: unknown, path: string) => void | {
9
+ replacer?: (this: Iterable, key: string, value: unknown) => void | {
10
10
  replacement: unknown;
11
11
  };
12
12
  }): string;
13
- type JsonSerializerError = Error & {
13
+ type ErrAddendum = {
14
14
  messageCore: string;
15
+ value: unknown;
16
+ path: Path;
17
+ pathString: string;
15
18
  };
16
- declare function isJsonSerializerError(thing: unknown): thing is JsonSerializerError;
19
+ declare function isJsonSerializerError(thing: unknown): thing is Error & ErrAddendum;
@@ -5,27 +5,43 @@ import { isReactElement } from './utils/isReactElement';
5
5
  import { isCallable } from './utils/isCallable';
6
6
  import { isObject } from './utils/isObject';
7
7
  import { replacerWithPath } from './utils/replacerWithPath';
8
- function stringify(value, { forbidReactElements, space, valueName, sortObjectKeys, replacer: replacerUserProvided, } = {}) {
8
+ function stringify(value, { forbidReactElements, space, valueName, sortObjectKeys,
9
+ // Used by Vike: https://github.com/vikejs/vike/blob/b4ba6b70e6bdc2e1f460c0d2e4c3faae5d0a733c/vike/node/plugin/plugins/importUserCode/v1-design/getConfigValuesSerialized.ts#L78
10
+ replacer: replacerUserProvided, } = {}) {
11
+ const canBeFirstKey = !valueName;
9
12
  // The only error `JSON.stringify()` can throw is `TypeError "cyclic object value"`.
10
13
  // - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#exceptions
11
14
  // - This means we have total of 3 possible errors while serializing:
12
15
  // - Cyclic references
13
16
  // - Functions
14
17
  // - React elements
15
- const serializer = (val) => JSON.stringify(val, replacerWithPath(replacer, !valueName), space);
18
+ const serializer = (val) => JSON.stringify(val, replacerWithPath(replacer), space);
16
19
  return serializer(value);
17
20
  function replacer(key, value, path) {
18
21
  {
19
- const ret = replacerUserProvided?.call(this, key, value, path);
22
+ const ret = replacerUserProvided?.call(this, key, value);
20
23
  if (ret)
21
24
  return ret.replacement;
22
25
  }
23
26
  if (forbidReactElements && isReactElement(value)) {
24
- throw genErr(genErrMsg('React element', path, valueName));
27
+ throw genErr({
28
+ value,
29
+ valueType: 'React element',
30
+ path,
31
+ canBeFirstKey,
32
+ rootValueName: valueName,
33
+ });
25
34
  }
26
35
  if (isCallable(value)) {
27
36
  const functionName = value.name;
28
- throw genErr(genErrMsg('function', path, valueName, path.length === 0 ? functionName : undefined));
37
+ throw genErr({
38
+ value,
39
+ valueType: 'function',
40
+ path,
41
+ canBeFirstKey,
42
+ rootValueName: valueName,
43
+ problematicValueName: path.length === 0 ? functionName : undefined,
44
+ });
29
45
  }
30
46
  const valueOriginal = this[key];
31
47
  for (const { is, serialize } of types.slice().reverse()) {
@@ -46,21 +62,27 @@ function stringify(value, { forbidReactElements, space, valueName, sortObjectKey
46
62
  return value;
47
63
  }
48
64
  }
49
- const stamp = '_isJsonSerializerError';
50
- function genErr(errMsg) {
65
+ function genErr({ value, valueType, path, canBeFirstKey, rootValueName, problematicValueName, }) {
66
+ const pathString = getPathString(path, canBeFirstKey);
67
+ const errMsg = getErrMsg({ valueType, pathString, rootValueName, problematicValueName });
51
68
  const err = new Error(`[@brillout/json-serializer](https://github.com/brillout/json-serializer) ${errMsg}.`);
52
- Object.assign(err, {
69
+ const errAddendum = {
53
70
  messageCore: errMsg,
54
71
  [stamp]: true,
55
- });
72
+ value,
73
+ path,
74
+ pathString,
75
+ };
76
+ Object.assign(err, errAddendum);
56
77
  return err;
57
78
  }
79
+ const stamp = '_isJsonSerializerError';
58
80
  function isJsonSerializerError(thing) {
59
81
  return isObject(thing) && thing[stamp] === true;
60
82
  }
61
- function genErrMsg(valueType, path, rootValueName, problematicValueName) {
83
+ function getErrMsg({ valueType, rootValueName, pathString, problematicValueName, }) {
62
84
  let subject;
63
- if (!path) {
85
+ if (!pathString) {
64
86
  subject = rootValueName || problematicValueName || 'value';
65
87
  }
66
88
  else {
@@ -70,7 +92,27 @@ function genErrMsg(valueType, path, rootValueName, problematicValueName) {
70
92
  else {
71
93
  subject = '';
72
94
  }
73
- subject = subject + (rootValueName || '') + path;
95
+ subject = subject + (rootValueName || '') + pathString;
74
96
  }
75
97
  return `cannot serialize ${subject} because it's a ${valueType}`;
76
98
  }
99
+ function getPathString(path, canBeFirstKey) {
100
+ const pathString = path
101
+ .map((key, i) => {
102
+ if (typeof key === 'number') {
103
+ return `[${key}]`;
104
+ }
105
+ if (i === 0 && canBeFirstKey && isKeyDotNotationCompatible(key)) {
106
+ return key;
107
+ }
108
+ return getPropAccessNotation(key);
109
+ })
110
+ .join('');
111
+ return pathString;
112
+ }
113
+ function getPropAccessNotation(key) {
114
+ return typeof key === 'string' && isKeyDotNotationCompatible(key) ? `.${key}` : `[${JSON.stringify(key)}]`;
115
+ }
116
+ function isKeyDotNotationCompatible(key) {
117
+ return /^[a-z0-9\$_]+$/i.test(key);
118
+ }
@@ -1 +0,0 @@
1
- export declare function isKeyDotNotationCompatible(key: string): boolean;
@@ -1,3 +1 @@
1
- export function isKeyDotNotationCompatible(key) {
2
- return /^[a-z0-9\$_]+$/i.test(key);
3
- }
1
+ "use strict";
@@ -1,5 +1,7 @@
1
1
  export { replacerWithPath };
2
2
  export type { Iterable };
3
+ export type { Path };
4
+ type Path = (string | number)[];
3
5
  type Iterable = Record<string, unknown>;
4
- type Replacer = (this: Iterable, key: string, value: unknown, path: string) => unknown;
5
- declare function replacerWithPath(replacer: Replacer, canBeFirstKey: boolean): (this: Iterable, key: string, value: unknown) => unknown;
6
+ type Replacer = (this: Iterable, key: string, value: unknown, path: Path) => unknown;
7
+ declare function replacerWithPath(replacer: Replacer): (this: Iterable, key: string, value: unknown) => unknown;
@@ -1,23 +1,18 @@
1
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();
2
+ function replacerWithPath(replacer) {
3
+ const pathMap = new WeakMap();
6
4
  return function (key, value) {
7
- const prefix = paths.get(this);
8
- const path = (prefix ?? '') + (key ? getPropAccessNotation(key, this, !prefix && canBeFirstKey) : '');
5
+ const pathPrevious = pathMap.get(this) ?? [];
6
+ const path = [...pathPrevious];
7
+ if (key !== '') {
8
+ const pathEntry = !Array.isArray(this) ? key : parseInt(key, 10);
9
+ path.push(pathEntry);
10
+ }
9
11
  if (isIterable(value))
10
- paths.set(value, path);
12
+ pathMap.set(value, path);
11
13
  return replacer.call(this, key, value, path);
12
14
  };
13
15
  }
14
16
  function isIterable(value) {
15
17
  return value === Object(value);
16
18
  }
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@brillout/json-serializer",
3
- "version": "0.5.10",
3
+ "version": "0.5.11",
4
4
  "description": "Same as JSON but with added support for `Date`, `undefined`, `Map`, `Set`, and more.",
5
5
  "main": "./index.mjs",
6
6
  "exports": {
@@ -29,22 +29,20 @@
29
29
  "test": "vitest",
30
30
  "========= Build readme.md": "",
31
31
  "docs": "mdocs",
32
- "========= Clean": "",
33
- "// Remove all generated files": "",
34
- "clean": "git clean -Xdf",
35
- "reset": "pnpm run clean && pnpm install && pnpm run build",
36
32
  "========= Formatting": "",
37
33
  "format": "pnpm run format:biome",
38
34
  "format:prettier": "git ls-files | egrep '\\.(json|js|jsx|css|ts|tsx|vue|mjs|cjs)$' | grep --invert-match package.json | xargs pnpm exec prettier --write",
39
35
  "format:biome": "biome format --write .",
40
36
  "format:check": "biome format . || echo Fix formatting by running: $ pnpm -w run format",
41
37
  "========= Release": "",
42
- "release": "release-me patch"
38
+ "release": "release-me patch",
39
+ "========= Reset": "",
40
+ "reset": "git clean -Xdf && pnpm install && pnpm run build"
43
41
  },
44
42
  "devDependencies": {
45
43
  "@biomejs/biome": "^1.7.2",
46
44
  "@brillout/mdocs": "^0.1.30",
47
- "@brillout/release-me": "^0.3.7",
45
+ "@brillout/release-me": "^0.3.8",
48
46
  "@types/node": "^20.5.6",
49
47
  "@types/react": "^18.2.21",
50
48
  "lodash.isequal": "^4.5.0",
@@ -53,7 +51,6 @@
53
51
  "typescript": "^5.2.2",
54
52
  "vitest": "^0.34.3"
55
53
  },
56
- "packageManager": "pnpm@8.6.12",
57
54
  "// Use @vitest/snapshot PR https://github.com/vitest-dev/vitest/pull/3961": "",
58
55
  "pnpm": {
59
56
  "overrides": {
package/parse.js CHANGED
@@ -8,4 +8,4 @@
8
8
  // prettier-ignore
9
9
  // biome-ignore format:
10
10
  exports.parse = require('./dist/cjs/parse.js').parse;
11
- exports.parseTransform = require('./dist/cjs/parse.js').parseTransform;
11
+ exports.parseTransform = require('./dist/cjs/parse.js').parseTransform