Skip to content

Commit

Permalink
Added json option too.
Browse files Browse the repository at this point in the history
  • Loading branch information
WebReflection committed Nov 2, 2021
1 parent 9afbc87 commit a4c8bd4
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 183 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,7 @@ const cloned = structuredClone({
```

The behavior is the same found in *JSON* when it comes to *Array*, so that unsupported values will result as `null` placeholders instead.

#### toJSON

If `lossy` option is not enough, `json` will actually enforce `lossy` and also check for `toJSON` method when objects are parsed.
179 changes: 88 additions & 91 deletions cjs/serialize.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,104 +43,100 @@ const shouldSkip = ([TYPE, type]) => (
(type === 'function' || type === 'symbol')
);

const as = (out, value, $, _) => {
const index = _.push(out) - 1;
$.set(value, index);
return index;
};

const pair = (lossy, value, $, _) => {
if ($.has(value))
return $.get(value);

let [TYPE, type] = typeOf(value);
switch (TYPE) {
case PRIMITIVE: {
let entry = value;
switch (type) {
case 'bigint':
TYPE = BIGINT;
entry = value.toString();
break;
case 'function':
case 'symbol':
if (!lossy)
throw new TypeError('unable to serialize ' + type);
entry = null;
break;
}
return as([TYPE, entry], value, $, _);
}
case ARRAY: {
if (type)
return as([type, [...value]], value, $, _);

const arr = [];
const index = as([TYPE, arr], value, $, _);
for (const entry of value)
arr.push(pair(lossy, entry, $, _));
return index;
}
case OBJECT: {
if (type) {
const serializer = (strict, json, $, _) => {

const as = (out, value) => {
const index = _.push(out) - 1;
$.set(value, index);
return index;
};

const pair = value => {
if ($.has(value))
return $.get(value);

let [TYPE, type] = typeOf(value);
switch (TYPE) {
case PRIMITIVE: {
let entry = value;
switch (type) {
case 'BigInt':
return as([type, value.toString()], value, $, _);
case 'Boolean':
case 'Number':
case 'String':
return as([type, value.valueOf()], value, $, _);
case 'bigint':
TYPE = BIGINT;
entry = value.toString();
break;
case 'function':
case 'symbol':
if (strict)
throw new TypeError('unable to serialize ' + type);
entry = null;
break;
}
return as([TYPE, entry], value);
}
case ARRAY: {
if (type)
return as([type, [...value]], value);

const arr = [];
const index = as([TYPE, arr], value);
for (const entry of value)
arr.push(pair(entry));
return index;
}
case OBJECT: {
if (type) {
switch (type) {
case 'BigInt':
return as([type, value.toString()], value);
case 'Boolean':
case 'Number':
case 'String':
return as([type, value.valueOf()], value);
}
}

const entries = [];
const index = as([TYPE, entries], value, $, _);
for (const key of keys(value)) {
if (lossy && shouldSkip(typeOf(value[key])))
continue;
if (json && ('toJSON' in value))
return pair(value.toJSON());

entries.push([
pair(lossy, key, $, _),
pair(lossy, value[key], $, _)
]);
const entries = [];
const index = as([TYPE, entries], value);
for (const key of keys(value)) {
if (strict || !shouldSkip(typeOf(value[key])))
entries.push([pair(key), pair(value[key])]);
}
return index;
}
return index;
}
case DATE:
return as([TYPE, value.toISOString()], value, $, _);
case REGEXP: {
const {source, flags} = value;
return as([TYPE, {source, flags}], value, $, _);
}
case MAP: {
const entries = [];
const index = as([TYPE, entries], value, $, _);
for (const [key, entry] of value) {
if (lossy && (shouldSkip(typeOf(key)) || shouldSkip(typeOf(entry))))
continue;

entries.push([
pair(lossy, key, $, _),
pair(lossy, entry, $, _)
]);
case DATE:
return as([TYPE, value.toISOString()], value);
case REGEXP: {
const {source, flags} = value;
return as([TYPE, {source, flags}], value);
}
return index;
}
case SET: {
const entries = [];
const index = as([TYPE, entries], value, $, _);
for (const entry of value) {
if (lossy && shouldSkip(typeOf(entry)))
continue;

entries.push(pair(lossy, entry, $, _));
case MAP: {
const entries = [];
const index = as([TYPE, entries], value);
for (const [key, entry] of value) {
if (strict || !(shouldSkip(typeOf(key)) || shouldSkip(typeOf(entry))))
entries.push([pair(key), pair(entry)]);
}
return index;
}
case SET: {
const entries = [];
const index = as([TYPE, entries], value);
for (const entry of value) {
if (strict || !shouldSkip(typeOf(entry)))
entries.push(pair(entry));
}
return index;
}
return index;
}
}

const {message} = value;
return as([TYPE, {name: type, message}], value, $, _);
const {message} = value;
return as([TYPE, {name: type, message}], value);
};

return pair;
};

/**
Expand All @@ -149,14 +145,15 @@ const pair = (lossy, value, $, _) => {

/**
* Returns an array of serialized Records.
* @param {any} serializable a serializable value.
* @param {any} value a serializable value.
* @param {{lossy?: boolean}?} options an object with a `lossy` property that,
* if `true`, will not throw errors on incompatible types, and behave more
* like JSON stringify would behave. Symbol and Function will be discarded.
* @returns {Record[]}
*/
const serialize = (serializable, options = {}) => {
const serialize = (value, options = {}) => {
const _ = [];
return pair(!!options.lossy, serializable, new Map, _), _;
const json = !!options.json;
return serializer(!(json || options.lossy), json, new Map, _)(value), _;
};
exports.serialize = serialize;
Loading

0 comments on commit a4c8bd4

Please sign in to comment.