Skip to content

Commit

Permalink
feat: add dedupe flag (#247)
Browse files Browse the repository at this point in the history
* add failing test

* add failing test

* tweak

* fix test

* Update src/plainer.ts

* Update src/plainer.ts

* less diff

* feat: add dedupe flag

* rm useless test

* refactor: name dedupeReferentialEqualities

* fix: dedupe is enough

* fix: test

* fix: test

---------

Co-authored-by: Simon Knott <info@simonknott.de>
  • Loading branch information
KATT and Skn0tt authored Jul 17, 2023
1 parent 601fc26 commit dd3aeb5
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 5 deletions.
36 changes: 36 additions & 0 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1157,3 +1157,39 @@ test('regression #245: superjson referential equalities only use the top-most pa
const parsed = SuperJSON.deserialize(res);
expect(parsed).toEqual(input);
});

test('dedupe=true', () => {
const instance = new SuperJSON({
dedupe: true,
});

type Node = {
children: Node[];
};
const root: Node = {
children: [],
};
const input = {
a: root,
b: root,
};
const output = instance.serialize(input);

const json = output.json as any;

expect(json.a);

// This has already been seen and should be deduped
expect(json.b).toBeNull();

expect(json).toMatchInlineSnapshot(`
Object {
"a": Object {
"children": Array [],
},
"b": null,
}
`);

expect(instance.deserialize(output)).toEqual(input);
});
22 changes: 19 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,38 @@
import { SuperJSONResult, SuperJSONValue, Class, JSONValue } from './types';
import { Class, JSONValue, SuperJSONResult, SuperJSONValue } from './types';
import { ClassRegistry, RegisterOptions } from './class-registry';
import { Registry } from './registry';
import {
CustomTransfomer,
CustomTransformerRegistry,
} from './custom-transformer-registry';
import {
walker,
applyReferentialEqualityAnnotations,
applyValueAnnotations,
generateReferentialEqualityAnnotations,
walker,
} from './plainer';
import { copy } from 'copy-anything';

export default class SuperJSON {
/**
* If true, SuperJSON will make sure only one instance of referentially equal objects are serialized and the rest are replaced with `null`.
*/
private readonly dedupe: boolean;

/**
* @param dedupeReferentialEqualities If true, SuperJSON will make sure only one instance of referentially equal objects are serialized and the rest are replaced with `null`.
*/
constructor({
dedupe = false,
}: {
dedupe?: boolean;
} = {}) {
this.dedupe = dedupe;
}

serialize(object: SuperJSONValue): SuperJSONResult {
const identities = new Map<any, any[][]>();
const output = walker(object, identities, this);
const output = walker(object, identities, this, this.dedupe);
const res: SuperJSONResult = {
json: output.transformedValue,
};
Expand Down
3 changes: 2 additions & 1 deletion src/plainer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ test('walker', () => {
b: /test/g,
},
new Map(),
new SuperJSON()
new SuperJSON(),
false
)
).toEqual({
transformedValue: {
Expand Down
8 changes: 7 additions & 1 deletion src/plainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ export const walker = (
object: any,
identities: Map<any, any[][]>,
superJson: SuperJSON,
dedupe: boolean,
path: any[] = [],
objectsInThisPath: any[] = [],
seenObjects = new Map<unknown, Result>()
Expand All @@ -166,7 +167,11 @@ export const walker = (
const seen = seenObjects.get(object);
if (seen) {
// short-circuit result if we've seen this object before
return seen;
return dedupe
? {
transformedValue: null,
}
: seen;
}
}

Expand Down Expand Up @@ -205,6 +210,7 @@ export const walker = (
value,
identities,
superJson,
dedupe,
[...path, index],
[...objectsInThisPath, object],
seenObjects
Expand Down

0 comments on commit dd3aeb5

Please sign in to comment.