Comparing version 4.2.3 to 4.3.0
{ | ||
"name": "devalue", | ||
"description": "Gets the job done when JSON.stringify can't", | ||
"version": "4.2.3", | ||
"version": "4.3.0", | ||
"repository": "Rich-Harris/devalue", | ||
@@ -6,0 +6,0 @@ "exports": { |
@@ -12,2 +12,3 @@ # devalue | ||
- `BigInt` | ||
- custom types via replacers, reducers and revivers | ||
@@ -25,3 +26,3 @@ Try it out [here](https://svelte.dev/repl/138d70def7a748ce9eda736ef1c71239?version=3.49.0). | ||
- Human-readable output | ||
- Stringifying functions or non-POJOs | ||
- Stringifying functions | ||
@@ -83,5 +84,48 @@ ## Usage | ||
## Custom types | ||
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_: | ||
```js | ||
class Vector { | ||
constructor(x, y) { | ||
this.x = x; | ||
this.y = y; | ||
} | ||
magnitude() { | ||
return Math.sqrt(this.x * this.x + this.y * this.y); | ||
} | ||
} | ||
const stringified = devalue.stringify(new Vector(30, 40), { | ||
Vector: (value) => value instanceof Vector && [value.x, value.y] | ||
}); | ||
console.log(stringified); // [["Vector",1],[2,3],30,40] | ||
const vector = devalue.parse(stringified, { | ||
Vector: ([x, y]) => new Vector(x, y) | ||
}); | ||
console.log(vector.magnitude()); // 50 | ||
``` | ||
If a function passed to `stringify` returns a truthy value, it's treated as a match. | ||
You can also use custom types with `uneval` by specifying a custom replacer: | ||
```js | ||
devalue.uneval(vector, (value, uneval) => { | ||
if (value instanceof Vector) { | ||
return `new Vector(${value.x},${value.y})`; | ||
} | ||
}); // `new Vector(30,40)` | ||
``` | ||
Note that any variables referenced in the resulting JavaScript (like `Vector` in the example above) must be in scope when it runs. | ||
## Error handling | ||
If `uneval` or `stringify` encounters a function or a non-POJO, it will throw an error. You can find where in the input data the offending value lives by inspecting `error.path`: | ||
If `uneval` or `stringify` encounters a function or a non-POJO that isn't handled by a custom replacer/reducer, it will throw an error. You can find where in the input data the offending value lives by inspecting `error.path`: | ||
@@ -180,2 +224,3 @@ ```js | ||
- [superjson](https://github.com/blitz-js/superjson) by Blitz | ||
- [next-json](https://github.com/iccicci/next-json) by Daniele Ricci | ||
@@ -182,0 +227,0 @@ ## License |
@@ -13,12 +13,14 @@ import { | ||
* @param {string} serialized | ||
* @param {Record<string, (value: any) => any>} [revivers] | ||
*/ | ||
export function parse(serialized) { | ||
return unflatten(JSON.parse(serialized)); | ||
export function parse(serialized, revivers) { | ||
return unflatten(JSON.parse(serialized), revivers); | ||
} | ||
/** | ||
* Revive a value flattened with `devalue.flatten` | ||
* Revive a value flattened with `devalue.stringify` | ||
* @param {number | any[]} parsed | ||
* @param {Record<string, (value: any) => any>} [revivers] | ||
*/ | ||
export function unflatten(parsed) { | ||
export function unflatten(parsed, revivers) { | ||
if (typeof parsed === 'number') return hydrate(parsed, true); | ||
@@ -34,3 +36,6 @@ | ||
/** @param {number} index */ | ||
/** | ||
* @param {number} index | ||
* @returns {any} | ||
*/ | ||
function hydrate(index, standalone = false) { | ||
@@ -55,2 +60,7 @@ if (index === UNDEFINED) return undefined; | ||
const reviver = revivers?.[type]; | ||
if (reviver) { | ||
return (hydrated[index] = reviver(hydrate(value[1]))); | ||
} | ||
switch (type) { | ||
@@ -96,2 +106,5 @@ case 'Date': | ||
break; | ||
default: | ||
throw new Error(`Unknown type ${type}`); | ||
} | ||
@@ -98,0 +111,0 @@ } else { |
@@ -20,4 +20,5 @@ import { | ||
* @param {any} value | ||
* @param {Record<string, (value: any) => any>} [reducers] | ||
*/ | ||
export function stringify(value) { | ||
export function stringify(value, reducers) { | ||
/** @type {any[]} */ | ||
@@ -29,2 +30,8 @@ const stringified = []; | ||
/** @type {Array<{ key: string, fn: (value: any) => any }>} */ | ||
const custom = []; | ||
for (const key in reducers) { | ||
custom.push({ key, fn: reducers[key] }); | ||
} | ||
/** @type {string[]} */ | ||
@@ -52,2 +59,10 @@ const keys = []; | ||
for (const { key, fn } of custom) { | ||
const value = fn(thing); | ||
if (value) { | ||
stringified[index] = `["${key}",${flatten(value)}]`; | ||
return index; | ||
} | ||
} | ||
let str = ''; | ||
@@ -54,0 +69,0 @@ |
@@ -18,4 +18,5 @@ import { | ||
* @param {any} value | ||
* @param {(value: any) => string | void} [replacer] | ||
*/ | ||
export function uneval(value) { | ||
export function uneval(value, replacer) { | ||
const counts = new Map(); | ||
@@ -26,2 +27,4 @@ | ||
const custom = new Map(); | ||
/** @param {any} thing */ | ||
@@ -41,2 +44,11 @@ function walk(thing) { | ||
if (replacer) { | ||
const str = replacer(thing); | ||
if (typeof str === 'string') { | ||
custom.set(thing, str); | ||
return; | ||
} | ||
} | ||
const type = get_type(thing); | ||
@@ -123,2 +135,6 @@ | ||
if (custom.has(thing)) { | ||
return custom.get(thing); | ||
} | ||
const type = get_type(thing); | ||
@@ -181,2 +197,7 @@ | ||
if (custom.has(thing)) { | ||
values.push(/** @type {string} */ (custom.get(thing))); | ||
return; | ||
} | ||
if (is_primitive(thing)) { | ||
@@ -183,0 +204,0 @@ values.push(stringify_primitive(thing)); |
/** | ||
* Revive a value serialized with `devalue.stringify` | ||
* @param {string} serialized | ||
* @param {Record<string, (value: any) => any>} [revivers] | ||
*/ | ||
export function parse(serialized: string): any; | ||
export function parse(serialized: string, revivers?: Record<string, (value: any) => any>): any; | ||
/** | ||
* Revive a value flattened with `devalue.flatten` | ||
* Revive a value flattened with `devalue.stringify` | ||
* @param {number | any[]} parsed | ||
* @param {Record<string, (value: any) => any>} [revivers] | ||
*/ | ||
export function unflatten(parsed: number | any[]): any; | ||
export function unflatten(parsed: number | any[], revivers?: Record<string, (value: any) => any>): any; |
/** | ||
* Turn a value into a JSON string that can be parsed with `devalue.parse` | ||
* @param {any} value | ||
* @param {Record<string, (value: any) => any>} [reducers] | ||
*/ | ||
export function stringify(value: any): string; | ||
export function stringify(value: any, reducers?: Record<string, (value: any) => any>): string; |
/** | ||
* Turn a value into the JavaScript that creates an equivalent value | ||
* @param {any} value | ||
* @param {(value: any) => string | void} [replacer] | ||
*/ | ||
export function uneval(value: any): string; | ||
export function uneval(value: any, replacer?: (value: any) => string | void): string; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
26284
663
226