Skip to content

Commit a4fe876

Browse files
committed
Merge branch 'main' into pr/69
2 parents 4a2b8ca + 566b961 commit a4fe876

File tree

8 files changed

+79
-10
lines changed

8 files changed

+79
-10
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
strategy:
1919
fail-fast: false
2020
matrix:
21-
node-version: [16]
21+
node-version: [16, 18, 20]
2222
os: [ubuntu-latest]
2323
steps:
2424
- run: git config --global core.autocrlf false

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# devalue changelog
22

3+
## 5.0.0
4+
5+
- Ignore non-enumerable symbolic keys ([#78](https://github.com/Rich-Harris/devalue/pull/78))
6+
7+
## 4.3.3
8+
9+
- Support invalid dates ([#61](https://github.com/Rich-Harris/devalue/pull/61))
10+
- Fix incorrect `error.path` when object contains a map ([#64](https://github.com/Rich-Harris/devalue/pull/64))
11+
312
## 4.3.2
413

514
- Better type declarations ([#66](https://github.com/Rich-Harris/devalue/pull/66))

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ const data = devalue.unflatten(JSON.parse(json).data);
8282

8383
## Custom types
8484

85-
You can serialize and serialize custom types by passing a second argument to `stringify` containing an object of types and their _reducers_, and a second argument to `parse` or `unflatten` containing an object of types and their _revivers_:
85+
You can serialize and deserialize custom types by passing a second argument to `stringify` containing an object of types and their _reducers_, and a second argument to `parse` or `unflatten` containing an object of types and their _revivers_:
8686

8787
```js
8888
class Vector {

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
{
22
"name": "devalue",
33
"description": "Gets the job done when JSON.stringify can't",
4-
"version": "4.3.2",
4+
"version": "5.0.0",
55
"repository": "Rich-Harris/devalue",
6+
"sideEffects": false,
67
"exports": {
78
".": {
89
"types": "./types/index.d.ts",
@@ -25,9 +26,9 @@
2526
"scripts": {
2627
"build": "dts-buddy",
2728
"test": "uvu test",
28-
"prepublishOnly": "npm test && publint && npm run build"
29+
"prepublishOnly": "npm test && npm run build && publint"
2930
},
3031
"license": "MIT",
3132
"type": "module",
3233
"packageManager": "[email protected]"
33-
}
34+
}

src/stringify.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
DevalueError,
3+
enumerable_symbols,
34
get_type,
45
is_plain_object,
56
is_primitive,
@@ -82,7 +83,8 @@ export function stringify(value, reducers) {
8283
break;
8384

8485
case 'Date':
85-
str = `["Date","${thing.toISOString()}"]`;
86+
const valid = !isNaN(thing.getDate());
87+
str = `["Date","${valid ? thing.toISOString() : ''}"]`;
8688
break;
8789

8890
case 'RegExp':
@@ -129,6 +131,7 @@ export function stringify(value, reducers) {
129131
`.get(${is_primitive(key) ? stringify_primitive(key) : '...'})`
130132
);
131133
str += `,${flatten(key)},${flatten(value)}`;
134+
keys.pop();
132135
}
133136

134137
str += ']';
@@ -169,7 +172,7 @@ export function stringify(value, reducers) {
169172
);
170173
}
171174

172-
if (Object.getOwnPropertySymbols(thing).length > 0) {
175+
if (enumerable_symbols(thing).length > 0) {
173176
throw new DevalueError(
174177
`Cannot stringify POJOs with symbolic keys`,
175178
keys

src/uneval.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
DevalueError,
3+
enumerable_symbols,
34
escaped,
45
get_type,
56
is_plain_object,
@@ -105,7 +106,7 @@ export function uneval(value, replacer) {
105106
);
106107
}
107108

108-
if (Object.getOwnPropertySymbols(thing).length > 0) {
109+
if (enumerable_symbols(thing).length > 0) {
109110
throw new DevalueError(
110111
`Cannot stringify POJOs with symbolic keys`,
111112
keys

src/utils.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,10 @@ export function stringify_string(str) {
9797

9898
return `"${last_pos === 0 ? str : result + str.slice(last_pos)}"`;
9999
}
100+
101+
/** @param {Record<string | symbol, any>} object */
102+
export function enumerable_symbols(object) {
103+
return Object.getOwnPropertySymbols(object).filter(
104+
(symbol) => Object.getOwnPropertyDescriptor(object, symbol).enumerable
105+
);
106+
}

test/test.js

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ class Custom {
99
}
1010
}
1111

12+
const node_version = +process.versions.node.split('.')[0];
13+
1214
const fixtures = {
1315
basics: [
1416
{
@@ -107,6 +109,15 @@ const fixtures = {
107109
js: 'new Date(1000000000000)',
108110
json: '[["Date","2001-09-09T01:46:40.000Z"]]'
109111
},
112+
{
113+
name: 'invalid Date',
114+
value: new Date(''),
115+
js: 'new Date(NaN)',
116+
json: '[["Date",""]]',
117+
validate: (value) => {
118+
assert.ok(isNaN(value.valueOf()));
119+
}
120+
},
110121
{
111122
name: 'Array',
112123
value: ['a', 'b', 'c'],
@@ -391,6 +402,19 @@ const fixtures = {
391402
assert.equal(Object.getPrototypeOf(value), Object.prototype);
392403
assert.equal(Object.keys(value).length, 0);
393404
}
405+
},
406+
{
407+
name: 'non-enumerable symbolic key',
408+
value: (() => {
409+
const obj = { x: 1 };
410+
Object.defineProperty(obj, Symbol('key'), {
411+
value: 'value',
412+
enumerable: false
413+
});
414+
return obj;
415+
})(),
416+
js: '{x:1}',
417+
json: '[{"x":1},1]'
394418
}
395419
],
396420

@@ -487,7 +511,10 @@ const invalid = [
487511
{
488512
name: 'invalid JSON',
489513
json: '][',
490-
message: 'Unexpected token ] in JSON at position 0'
514+
message:
515+
node_version >= 20
516+
? `Unexpected token ']', "][" is not valid JSON`
517+
: 'Unexpected token ] in JSON at position 0'
491518
},
492519
{
493520
name: 'hole',
@@ -530,7 +557,13 @@ for (const { name, json, message } of invalid) {
530557
uvu.test(`parse error: ${name}`, () => {
531558
assert.throws(
532559
() => parse(json),
533-
(error) => error.message === message
560+
(error) => {
561+
const match = error.message === message;
562+
if (!match) {
563+
console.error(`Expected: ${message}, got: ${error.message}`);
564+
}
565+
return match;
566+
}
534567
);
535568
});
536569
}
@@ -572,6 +605,21 @@ for (const fn of [uneval, stringify]) {
572605
assert.equal(e.path, '.foo.map.get("key")');
573606
}
574607
});
608+
609+
uvu.test(`${fn.name} populates error.path after maps (#64)`, () => {
610+
try {
611+
fn({
612+
map: new Map([['key', 'value']]),
613+
object: {
614+
invalid() {}
615+
}
616+
});
617+
} catch (e) {
618+
assert.equal(e.name, 'DevalueError');
619+
assert.equal(e.message, 'Cannot stringify a function');
620+
assert.equal(e.path, '.object.invalid');
621+
}
622+
});
575623
}
576624

577625
uvu.test('does not create duplicate parameter names', () => {

0 commit comments

Comments
 (0)