Counteract namespace minification by terser
This breaks serialized data compatibility !
This commit is contained in:
parent
9710424caf
commit
b9388a8f11
6 changed files with 103 additions and 14 deletions
|
@ -63,7 +63,7 @@ const obj = {
|
|||
let serializer = new Serializer(namespace);
|
||||
|
||||
// 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
|
||||
let state = serializer.serialize(obj);
|
||||
|
|
2
package-lock.json
generated
2
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "tk-serializer",
|
||||
"version": "1.1.2",
|
||||
"version": "1.2.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "tk-serializer",
|
||||
"version": "1.1.2",
|
||||
"version": "1.2.0",
|
||||
"description": "Typescript/Javascript serializer, with full object reconstruction",
|
||||
"author": {
|
||||
"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 = {
|
||||
TestSerializerObj1,
|
||||
TestSerializerObj2,
|
||||
TestSerializerObj3
|
||||
TestSerializerObj3,
|
||||
};
|
||||
|
||||
const TEST_NS_FLAT = [
|
||||
TestSerializerObj1,
|
||||
TestSerializerObj2,
|
||||
TestSerializerObj3,
|
||||
];
|
||||
|
||||
const TEST_NS_RENAME = {
|
||||
ob1: TestSerializerObj1,
|
||||
ob2: TestSerializerObj2,
|
||||
ob3: TestSerializerObj3,
|
||||
}
|
||||
|
||||
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 data = serializer.serialize(obj);
|
||||
serializer = new Serializer(namespace);
|
||||
|
@ -45,7 +60,15 @@ describe("Serializer", () => {
|
|||
});
|
||||
|
||||
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).toBeInstanceOf(TestSerializerObj1);
|
||||
});
|
||||
|
@ -73,11 +96,27 @@ describe("Serializer", () => {
|
|||
});
|
||||
|
||||
it("ignores some classes", () => {
|
||||
var serializer = new Serializer(TEST_NS);
|
||||
let serializer = new Serializer(TEST_NS);
|
||||
serializer.addIgnoredClass("TestSerializerObj1");
|
||||
|
||||
var data = serializer.serialize({ a: 5, b: new TestSerializerObj1() });
|
||||
var loaded = serializer.unserialize(data);
|
||||
let data = serializer.serialize({ a: 5, b: new TestSerializerObj1() });
|
||||
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 });
|
||||
});
|
||||
|
@ -102,4 +141,32 @@ describe("Serializer", () => {
|
|||
expected.a = [1, 2, 3];
|
||||
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;
|
||||
}
|
||||
|
||||
type SerializerNamespace = { [name: string]: { new(): any } };
|
||||
|
||||
/**
|
||||
* A deep serializer of javascript objects.
|
||||
*/
|
||||
export class Serializer {
|
||||
private namespace: any;
|
||||
private namespace: SerializerNamespace;
|
||||
private namespace_rev: { [classname: string]: string };
|
||||
private ignored: string[] = [];
|
||||
|
||||
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
|
||||
*/
|
||||
addIgnoredClass(name: string) {
|
||||
this.ignored.push(name);
|
||||
addIgnoredClass(cl: string | { new(): any }) {
|
||||
this.ignored.push(typeof cl === "string" ? cl : cl.name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -123,7 +138,7 @@ export class Serializer {
|
|||
//console.log("Serialize stats", stats);
|
||||
|
||||
// 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) => {
|
||||
if (key != "$f" && isObject(value) && !value.hasOwnProperty("$c") && !value.hasOwnProperty("$i")) {
|
||||
return { $i: objects.indexOf(value) };
|
||||
|
|
Loading…
Reference in a new issue