surrial
Version:
Serialize anything. This is surreal!
158 lines • 6.78 kB
JavaScript
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
Object.defineProperty(exports, "__esModule", { value: true });
var helpers_1 = require("./helpers");
var os_1 = require("os");
var UID = Math.floor(Math.random() * 0x10000000000).toString(16);
var PLACE_HOLDER_REGEXP = new RegExp('"@__' + UID + '-(\\d+)__@"', 'g');
var IS_NATIVE_CODE_REGEXP = /\{\s*\[native code\]\s*\}/g;
var BUILD_IN_SUPPORTED_CLASSES = Object.freeze([Map, Array, Buffer, Set, Date, RegExp]);
/**
* A surrial template tag, useful for building templates strings while enforcing the values to be serialized using surrial.
* @param templateLiterals The template literals
* @param values The values to be serialized using surrial
*/
function surrial(templateLiterals) {
var values = [];
for (var _i = 1; _i < arguments.length; _i++) {
values[_i - 1] = arguments[_i];
}
var stringBuilder = [];
for (var i = 0; i < values.length; i++) {
stringBuilder.push(templateLiterals[i]);
stringBuilder.push(serialize(values[i]));
}
stringBuilder.push(templateLiterals[templateLiterals.length - 1]);
return stringBuilder.join('');
}
exports.surrial = surrial;
/**
* Deserializes a string into it's javascript equivalent. CAUTION! Evaluates the string in the current javascript engine
* (`eval` or one of its friends). Be sure the `serializedThing` comes from a trusted source!
* @param serializedThing The string to deserialize
* @param knownClasses A list of known classes used to provide as constructor functions
*/
function deserialize(serializedThing, knownClasses) {
if (knownClasses === void 0) { knownClasses = []; }
var evalFn = new (Function.bind.apply(Function, __spreadArrays([void 0], knownClasses.map(function (t) { return t.name; }), ["\"use strict\";" + os_1.EOL + "return (" + serializedThing + ");"])))();
return evalFn.call.apply(evalFn, __spreadArrays([null], knownClasses));
}
exports.deserialize = deserialize;
/**
* Serializes the thing to a javascript string. This is NOT necessarily a JSON string, but will be valid javascript.
* @param thing The thing to be serialized
* @param knownClasses the classes of which instances are serialized as constructor calls (for example "new Person('Henry')").
*/
function serialize(thing, knownClasses) {
if (knownClasses === void 0) { knownClasses = []; }
if (thing instanceof Date) {
return serializeDate(thing);
}
else if (thing instanceof RegExp) {
return thing.toString();
}
else if (typeof thing === 'function') {
return serializeFunction(thing);
}
else if (thing instanceof Buffer) {
return serializeBuffer(thing);
}
else if (thing instanceof Set) {
return serializeSet(thing, knownClasses);
}
else if (thing instanceof Map) {
return serializeMap(thing, knownClasses);
}
else if (Array.isArray(thing)) {
return serializeArray(thing, knownClasses);
}
else if (helpers_1.isSurrializable(thing)) {
return thing.surrialize();
}
else if (helpers_1.isInstanceOf(thing, knownClasses)) {
return serializeClassInstance(thing, knownClasses);
}
else {
return stringifyObject(thing, knownClasses);
}
}
exports.serialize = serialize;
function serializeArray(thing, knownClasses) {
return stringifyObject(thing, knownClasses);
}
function stringifyObject(thing, knownClasses) {
var escapedValues = [];
// Returns placeholders anything JSON doesn't support (identified by index)
// which are later replaced by their string representation.
function replacer(key, value) {
// If the value is an object w/ a toJSON method, toJSON is called before
// the replacer runs, so we use this[key] to get the non-JSON'd value.
var origValue = this[key];
if (origValue !== thing &&
(helpers_1.isInstanceOf(origValue, BUILD_IN_SUPPORTED_CLASSES) || helpers_1.isInstanceOf(origValue, knownClasses) || helpers_1.isSurrializable(origValue))) {
return "@__" + UID + "-" + (escapedValues.push(origValue) - 1) + "__@";
}
else {
return value;
}
}
var str = JSON.stringify(thing, replacer);
// Protects against `JSON.stringify()` returning `undefined`, by serializing
// to the literal string: "undefined".
if (typeof str !== 'string') {
return String(str);
}
if (escapedValues.length === 0) {
return str;
}
else {
// Replaces all occurrences of placeholders in the
// JSON string with their string representations. If the original value can
// not be found, then `undefined` is used.
PLACE_HOLDER_REGEXP.lastIndex = 0;
return str.replace(PLACE_HOLDER_REGEXP, function (_, valueIndex) { return serialize(escapedValues[valueIndex], knownClasses); });
}
}
function serializeSet(value, knownClasses) {
var valuesArray = [];
value.forEach(function (v) { return valuesArray.push(serialize(v, knownClasses)); });
return "new Set([" + valuesArray.join(', ') + "])";
}
function serializeMap(map, knownClasses) {
var valuesArray = [];
map.forEach(function (value, key) { return valuesArray.push("[" + serialize(key, knownClasses) + ", " + serialize(value, knownClasses) + "]"); });
return "new Map([" + valuesArray.join(', ') + "])";
}
function serializeDate(value) {
return "new Date(\"" + value.toISOString() + "\")";
}
function serializeBuffer(value) {
return "Buffer.from(" + serialize(value.toString('binary')) + ", \"binary\")";
}
function serializeClassInstance(instance, knownClasses) {
var constructor = instance.constructor;
if (constructor.name.length) {
var params = helpers_1.getParamList(constructor);
var paramValues = params.map(function (param) { return serialize(instance[param], knownClasses); });
var newExpression = "new " + constructor.name + "(" + paramValues.join(', ') + ")";
return newExpression;
}
else {
throw new Error("Cannot serialize instances of nameless classes (class was defined as: " + constructor.toString() + ")");
}
}
function serializeFunction(fn) {
var serializedFn = fn.toString();
IS_NATIVE_CODE_REGEXP.lastIndex = 0;
if (IS_NATIVE_CODE_REGEXP.test(serializedFn)) {
throw new TypeError("Cannot serialize native function: " + fn.name);
}
return serializedFn;
}
//# sourceMappingURL=index.js.map
;