Counteract namespace minification by terser
This breaks serialized data compatibility !
This commit is contained in:
parent
9710424caf
commit
b9388a8f11
|
@ -63,7 +63,7 @@ const obj = {
|
||||||
let serializer = new Serializer(namespace);
|
let serializer = new Serializer(namespace);
|
||||||
|
|
||||||
// Optionally, some class instances may be ignored (they will be replaced by *undefined*)
|
// Optionally, some class instances may be ignored (they will be replaced by *undefined*)
|
||||||
serializer.addIgnoredClass("Class3");
|
serializer.addIgnoredClass(Class3);
|
||||||
|
|
||||||
// Serialize the object to a string
|
// Serialize the object to a string
|
||||||
let state = serializer.serialize(obj);
|
let state = serializer.serialize(obj);
|
||||||
|
|
2
package-lock.json
generated
2
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "tk-serializer",
|
"name": "tk-serializer",
|
||||||
"version": "1.1.2",
|
"version": "1.2.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "tk-serializer",
|
"name": "tk-serializer",
|
||||||
"version": "1.1.2",
|
"version": "1.2.0",
|
||||||
"description": "Typescript/Javascript serializer, with full object reconstruction",
|
"description": "Typescript/Javascript serializer, with full object reconstruction",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Michaël Lemaire",
|
"name": "Michaël Lemaire",
|
||||||
|
|
7
src/index.test.ts
Normal file
7
src/index.test.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import * as index from "./index";
|
||||||
|
|
||||||
|
describe("index", () => {
|
||||||
|
it("exposes Serializer", () => {
|
||||||
|
expect(index.Serializer).toBeTruthy();
|
||||||
|
});
|
||||||
|
})
|
|
@ -19,14 +19,29 @@ class TestSerializerObj3 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TestSerializerObj4 extends TestSerializerObj1 {
|
||||||
|
}
|
||||||
|
|
||||||
const TEST_NS = {
|
const TEST_NS = {
|
||||||
TestSerializerObj1,
|
TestSerializerObj1,
|
||||||
TestSerializerObj2,
|
TestSerializerObj2,
|
||||||
TestSerializerObj3
|
TestSerializerObj3,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const TEST_NS_FLAT = [
|
||||||
|
TestSerializerObj1,
|
||||||
|
TestSerializerObj2,
|
||||||
|
TestSerializerObj3,
|
||||||
|
];
|
||||||
|
|
||||||
|
const TEST_NS_RENAME = {
|
||||||
|
ob1: TestSerializerObj1,
|
||||||
|
ob2: TestSerializerObj2,
|
||||||
|
ob3: TestSerializerObj3,
|
||||||
|
}
|
||||||
|
|
||||||
describe("Serializer", () => {
|
describe("Serializer", () => {
|
||||||
function checkReversability(obj: any, namespace = TEST_NS): any {
|
function checkReversability(obj: any, namespace: any = TEST_NS): any {
|
||||||
var serializer = new Serializer(namespace);
|
var serializer = new Serializer(namespace);
|
||||||
var data = serializer.serialize(obj);
|
var data = serializer.serialize(obj);
|
||||||
serializer = new Serializer(namespace);
|
serializer = new Serializer(namespace);
|
||||||
|
@ -45,7 +60,15 @@ describe("Serializer", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("restores objects constructed from class", () => {
|
it("restores objects constructed from class", () => {
|
||||||
var loaded = checkReversability(new TestSerializerObj1(5));
|
let loaded = checkReversability(new TestSerializerObj1(5));
|
||||||
|
expect(loaded.a).toBe(5);
|
||||||
|
expect(loaded).toBeInstanceOf(TestSerializerObj1);
|
||||||
|
|
||||||
|
loaded = checkReversability(new TestSerializerObj1(5), TEST_NS_FLAT);
|
||||||
|
expect(loaded.a).toBe(5);
|
||||||
|
expect(loaded).toBeInstanceOf(TestSerializerObj1);
|
||||||
|
|
||||||
|
loaded = checkReversability(new TestSerializerObj1(5), TEST_NS_RENAME);
|
||||||
expect(loaded.a).toBe(5);
|
expect(loaded.a).toBe(5);
|
||||||
expect(loaded).toBeInstanceOf(TestSerializerObj1);
|
expect(loaded).toBeInstanceOf(TestSerializerObj1);
|
||||||
});
|
});
|
||||||
|
@ -73,11 +96,27 @@ describe("Serializer", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("ignores some classes", () => {
|
it("ignores some classes", () => {
|
||||||
var serializer = new Serializer(TEST_NS);
|
let serializer = new Serializer(TEST_NS);
|
||||||
serializer.addIgnoredClass("TestSerializerObj1");
|
serializer.addIgnoredClass("TestSerializerObj1");
|
||||||
|
|
||||||
var data = serializer.serialize({ a: 5, b: new TestSerializerObj1() });
|
let data = serializer.serialize({ a: 5, b: new TestSerializerObj1() });
|
||||||
var loaded = serializer.unserialize(data);
|
let loaded = serializer.unserialize(data);
|
||||||
|
|
||||||
|
expect(loaded).toEqual({ a: 5, b: undefined });
|
||||||
|
|
||||||
|
serializer = new Serializer(TEST_NS);
|
||||||
|
serializer.addIgnoredClass(TestSerializerObj1);
|
||||||
|
|
||||||
|
data = serializer.serialize({ a: 5, b: new TestSerializerObj1() });
|
||||||
|
loaded = serializer.unserialize(data);
|
||||||
|
|
||||||
|
expect(loaded).toEqual({ a: 5, b: undefined });
|
||||||
|
|
||||||
|
serializer = new Serializer(TEST_NS_RENAME);
|
||||||
|
serializer.addIgnoredClass(TestSerializerObj1);
|
||||||
|
|
||||||
|
data = serializer.serialize({ a: 5, b: new TestSerializerObj1() });
|
||||||
|
loaded = serializer.unserialize(data);
|
||||||
|
|
||||||
expect(loaded).toEqual({ a: 5, b: undefined });
|
expect(loaded).toEqual({ a: 5, b: undefined });
|
||||||
});
|
});
|
||||||
|
@ -102,4 +141,32 @@ describe("Serializer", () => {
|
||||||
expected.a = [1, 2, 3];
|
expected.a = [1, 2, 3];
|
||||||
expect(loaded).toEqual({ obj: expected });
|
expect(loaded).toEqual({ obj: expected });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("handles missing classes", () => {
|
||||||
|
jest.spyOn(console, "error").mockImplementation();
|
||||||
|
let serializer = new Serializer(TEST_NS);
|
||||||
|
let data = serializer.serialize({ obj: new TestSerializerObj4() });
|
||||||
|
let loaded = serializer.unserialize(data);
|
||||||
|
expect(loaded).toEqual({ obj: { a: 0 } });
|
||||||
|
expect(console.error).toHaveBeenCalledWith("Can't find class", "TestSerializerObj4");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses namespace alias to protect from property mangling", () => {
|
||||||
|
const data = {
|
||||||
|
a: new TestSerializerObj1(1),
|
||||||
|
b: new TestSerializerObj1(2),
|
||||||
|
c: [new TestSerializerObj1(3)],
|
||||||
|
};
|
||||||
|
|
||||||
|
const serializer1 = new Serializer(TEST_NS);
|
||||||
|
const serializer2 = new Serializer(TEST_NS_RENAME);
|
||||||
|
|
||||||
|
const dumped1 = serializer1.serialize(data);
|
||||||
|
const dumped2 = serializer2.serialize(data);
|
||||||
|
|
||||||
|
expect(dumped1.length).toBeGreaterThan(dumped2.length);
|
||||||
|
|
||||||
|
expect(serializer1.unserialize(dumped1)).toEqual(data);
|
||||||
|
expect(serializer2.unserialize(dumped2)).toEqual(data);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -62,22 +62,37 @@ function classname(obj: Object): string {
|
||||||
return (<any>obj.constructor).name;
|
return (<any>obj.constructor).name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SerializerNamespace = { [name: string]: { new(): any } };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A deep serializer of javascript objects.
|
* A deep serializer of javascript objects.
|
||||||
*/
|
*/
|
||||||
export class Serializer {
|
export class Serializer {
|
||||||
private namespace: any;
|
private namespace: SerializerNamespace;
|
||||||
|
private namespace_rev: { [classname: string]: string };
|
||||||
private ignored: string[] = [];
|
private ignored: string[] = [];
|
||||||
|
|
||||||
constructor(namespace: any) {
|
constructor(namespace: any) {
|
||||||
this.namespace = namespace;
|
if (Array.isArray(namespace)) {
|
||||||
|
this.namespace = {};
|
||||||
|
for (let construct of namespace) {
|
||||||
|
this.namespace[construct.name] = construct;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.namespace = namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.namespace_rev = {};
|
||||||
|
for (let [key, value] of Object.entries(this.namespace)) {
|
||||||
|
this.namespace_rev[value.name] = key;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a class to the ignore list
|
* Add a class to the ignore list
|
||||||
*/
|
*/
|
||||||
addIgnoredClass(name: string) {
|
addIgnoredClass(cl: string | { new(): any }) {
|
||||||
this.ignored.push(name);
|
this.ignored.push(typeof cl === "string" ? cl : cl.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -123,7 +138,7 @@ export class Serializer {
|
||||||
//console.log("Serialize stats", stats);
|
//console.log("Serialize stats", stats);
|
||||||
|
|
||||||
// Serialize objects list, transforming deeper objects to links
|
// Serialize objects list, transforming deeper objects to links
|
||||||
var fobjects = objects.map(value => <Object>{ $c: classname(value), $f: merge({}, value) });
|
var fobjects = objects.map(value => <Object>{ $c: this.namespace_rev[classname(value)] || classname(value), $f: merge({}, value) });
|
||||||
return JSON.stringify(fobjects, (key, value) => {
|
return JSON.stringify(fobjects, (key, value) => {
|
||||||
if (key != "$f" && isObject(value) && !value.hasOwnProperty("$c") && !value.hasOwnProperty("$i")) {
|
if (key != "$f" && isObject(value) && !value.hasOwnProperty("$c") && !value.hasOwnProperty("$i")) {
|
||||||
return { $i: objects.indexOf(value) };
|
return { $i: objects.indexOf(value) };
|
||||||
|
|
Loading…
Reference in a new issue