feat(redux-devtools-serialize): convert to TypeScript (#621)

* feature(redux-devtools-serialize): convert to TypeScript

* unused
This commit is contained in:
Nathan Bierema 2020-08-29 00:14:49 -04:00 committed by GitHub
parent 0663b7ce62
commit d586f1955a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 538 additions and 322 deletions

View File

@ -2,7 +2,7 @@ module.exports = {
extends: '../../.eslintrc',
overrides: [
{
files: ['*.ts', '*.tsx'],
files: ['*.ts'],
extends: '../../eslintrc.ts.base.json',
parserOptions: {
tsconfigRootDir: __dirname,
@ -10,7 +10,7 @@ module.exports = {
},
},
{
files: ['test/*.ts', 'test/*.tsx'],
files: ['test/*.ts'],
extends: '../../eslintrc.ts.jest.base.json',
parserOptions: {
tsconfigRootDir: __dirname,

View File

@ -36,7 +36,7 @@
"lint:fix": "eslint . --ext .ts,.tsx --fix",
"type-check": "tsc --noEmit",
"type-check:watch": "npm run type-check -- --watch",
"preversion": "npm run type-check && npm run lint && npm run test",
"preversion": "npm run type-check && npm run lint",
"prepublishOnly": "npm run clean && npm run build"
},
"dependencies": {

View File

@ -2,7 +2,7 @@ module.exports = {
extends: '../../.eslintrc',
overrides: [
{
files: ['*.ts', '*.tsx'],
files: ['*.ts'],
extends: '../../eslintrc.ts.base.json',
parserOptions: {
tsconfigRootDir: __dirname,
@ -10,7 +10,7 @@ module.exports = {
},
},
{
files: ['test/*.ts', 'test/*.tsx'],
files: ['test/*.ts'],
extends: '../../eslintrc.ts.jest.base.json',
parserOptions: {
tsconfigRootDir: __dirname,

View File

@ -32,8 +32,8 @@
"build:js": "babel src --out-dir lib --extensions \".ts\" --source-maps inline",
"clean": "rimraf lib",
"test": "jest",
"lint": "eslint . --ext .ts,.tsx",
"lint:fix": "eslint . --ext .ts,.tsx --fix",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"type-check": "tsc --noEmit",
"type-check:watch": "npm run type-check -- --watch",
"preversion": "npm run type-check && npm run lint && npm run test",

View File

@ -36,7 +36,7 @@
"lint:fix": "eslint . --ext .ts,.tsx --fix",
"type-check": "tsc --noEmit",
"type-check:watch": "npm run type-check -- --watch",
"preversion": "npm run type-check && npm run lint && npm run test",
"preversion": "npm run type-check && npm run lint",
"prepublishOnly": "npm run clean && npm run build"
},
"dependencies": {

View File

@ -0,0 +1,4 @@
{
"presets": ["@babel/preset-env", "@babel/preset-typescript"],
"plugins": ["@babel/plugin-proposal-class-properties"]
}

View File

@ -0,0 +1 @@
lib

View File

@ -0,0 +1,21 @@
module.exports = {
extends: '../../.eslintrc',
overrides: [
{
files: ['*.ts'],
extends: '../../eslintrc.ts.base.json',
parserOptions: {
tsconfigRootDir: __dirname,
project: ['./tsconfig.json'],
},
},
{
files: ['test/*.ts'],
extends: '../../eslintrc.ts.jest.base.json',
parserOptions: {
tsconfigRootDir: __dirname,
project: ['./test/tsconfig.json'],
},
},
],
};

View File

@ -1,32 +0,0 @@
function mark(data, type, transformMethod) {
return {
data: transformMethod ? data[transformMethod]() : data,
__serializedType__: type,
};
}
function extract(data, type) {
return {
data: Object.assign({}, data),
__serializedType__: type,
};
}
function refer(data, type, isArray, refs) {
var r = mark(data, type, isArray);
if (!refs) return r;
for (var i = 0; i < refs.length; i++) {
var ref = refs[i];
if (typeof ref === 'function' && data instanceof ref) {
r.__serializedRef__ = i;
return r;
}
}
return r;
}
module.exports = {
mark: mark,
extract: extract,
refer: refer,
};

View File

@ -1,23 +0,0 @@
var jsan = require('jsan');
var serialize = require('./serialize');
var options = require('../constants/options');
module.exports = function (Immutable, refs, customReplacer, customReviver) {
return {
stringify: function (data) {
return jsan.stringify(
data,
serialize(Immutable, refs, customReplacer, customReviver).replacer,
null,
options
);
},
parse: function (data) {
return jsan.parse(
data,
serialize(Immutable, refs, customReplacer, customReviver).reviver
);
},
serialize: serialize,
};
};

View File

@ -1,87 +0,0 @@
var helpers = require('../helpers');
var mark = helpers.mark;
var extract = helpers.extract;
var refer = helpers.refer;
var options = require('../constants/options');
module.exports = function serialize(
Immutable,
refs,
customReplacer,
customReviver
) {
function replacer(key, value) {
if (value instanceof Immutable.Record)
return refer(value, 'ImmutableRecord', 'toObject', refs);
if (value instanceof Immutable.Range)
return extract(value, 'ImmutableRange');
if (value instanceof Immutable.Repeat)
return extract(value, 'ImmutableRepeat');
if (Immutable.OrderedMap.isOrderedMap(value))
return mark(value, 'ImmutableOrderedMap', 'toObject');
if (Immutable.Map.isMap(value))
return mark(value, 'ImmutableMap', 'toObject');
if (Immutable.List.isList(value))
return mark(value, 'ImmutableList', 'toArray');
if (Immutable.OrderedSet.isOrderedSet(value))
return mark(value, 'ImmutableOrderedSet', 'toArray');
if (Immutable.Set.isSet(value))
return mark(value, 'ImmutableSet', 'toArray');
if (Immutable.Seq.isSeq(value))
return mark(value, 'ImmutableSeq', 'toArray');
if (Immutable.Stack.isStack(value))
return mark(value, 'ImmutableStack', 'toArray');
return value;
}
function reviver(key, value) {
if (
typeof value === 'object' &&
value !== null &&
'__serializedType__' in value
) {
var data = value.data;
switch (value.__serializedType__) {
case 'ImmutableMap':
return Immutable.Map(data);
case 'ImmutableOrderedMap':
return Immutable.OrderedMap(data);
case 'ImmutableList':
return Immutable.List(data);
case 'ImmutableRange':
return Immutable.Range(data._start, data._end, data._step);
case 'ImmutableRepeat':
return Immutable.Repeat(data._value, data.size);
case 'ImmutableSet':
return Immutable.Set(data);
case 'ImmutableOrderedSet':
return Immutable.OrderedSet(data);
case 'ImmutableSeq':
return Immutable.Seq(data);
case 'ImmutableStack':
return Immutable.Stack(data);
case 'ImmutableRecord':
return refs && refs[value.__serializedRef__]
? new refs[value.__serializedRef__](data)
: Immutable.Map(data);
default:
return data;
}
}
return value;
}
return {
replacer: customReplacer
? function (key, value) {
return customReplacer(key, value, replacer);
}
: replacer,
reviver: customReviver
? function (key, value) {
return customReviver(key, value, reviver);
}
: reviver,
options: options,
};
};

View File

@ -0,0 +1,3 @@
module.exports = {
preset: 'ts-jest',
};

View File

@ -2,29 +2,42 @@
"name": "redux-devtools-serialize",
"version": "0.1.9",
"description": "Serialize unserializable data and parse it back.",
"main": "index.js",
"scripts": {
"test": "jest --no-cache",
"prepublish": "npm run test"
},
"repository": {
"type": "git",
"url": "https://github.com/reduxjs/redux-devtools.git"
},
"keywords": [
"redux",
"devtools"
],
"author": "Mihail Diordiev <zalmoxisus@gmail.com> (https://github.com/zalmoxisus)",
"license": "MIT",
"homepage": "https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools-serialize",
"bugs": {
"url": "https://github.com/reduxjs/redux-devtools/issues"
},
"homepage": "https://github.com/reduxjs/redux-devtools",
"devDependencies": {
"immutable": "^4.0.0-rc.12"
"license": "MIT",
"author": "Mihail Diordiev <zalmoxisus@gmail.com> (https://github.com/zalmoxisus)",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/reduxjs/redux-devtools.git"
},
"scripts": {
"build": "npm run build:types && npm run build:js",
"build:types": "tsc --emitDeclarationOnly",
"build:js": "babel src --out-dir lib --extensions \".ts\" --source-maps inline",
"clean": "rimraf lib",
"test": "jest",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"type-check": "tsc --noEmit",
"type-check:watch": "npm run type-check -- --watch",
"preversion": "npm run type-check && npm run lint && npm run test",
"prepublishOnly": "npm run clean && npm run build"
},
"dependencies": {
"jsan": "^3.1.13"
},
"devDependencies": {
"immutable": "^4.0.0-rc.12"
},
"peerDependencies": {
"immutable": "^4.0.0-rc.12"
}
}

View File

@ -1,6 +1,6 @@
// jsan stringify options
module.exports = {
export default {
refs: false, // references can't be resolved on the original Immutable structure
date: true,
function: true,

View File

@ -0,0 +1,54 @@
export interface SerializedData {
data: unknown;
__serializedType__: string;
__serializedRef__?: number;
}
export function mark(data: unknown, type: string): SerializedData;
export function mark<K extends string>(
data: { [key in K]: () => unknown },
type: string,
transformMethod?: K | false
): SerializedData;
export function mark<K extends string>(
data: any,
type: string,
transformMethod?: 'toString' | false
): SerializedData;
export function mark<K extends string>(
data: { [key in K]: () => unknown } | unknown,
type: string,
transformMethod?: K | false
): SerializedData {
return {
data: transformMethod
? (data as { [key in K]: () => unknown })[transformMethod]()
: data,
__serializedType__: type,
};
}
export function extract(data: unknown, type: string): SerializedData {
return {
data: Object.assign({}, data),
__serializedType__: type,
};
}
export function refer<K extends string>(
data: { [key in K]: () => unknown },
type: string,
transformMethod: K | false,
refs?: (new (data: any) => unknown)[] | null
): SerializedData {
const r = mark(data, type, transformMethod);
if (!refs) return r;
for (let i = 0; i < refs.length; i++) {
const ref = refs[i];
if (typeof ref === 'function' && data instanceof ref) {
r.__serializedRef__ = i;
return r;
}
}
return r;
}

View File

@ -0,0 +1,37 @@
import jsan from 'jsan';
import Immutable from 'immutable';
import serialize from './serialize';
import options from '../constants/options';
export default function (
immutable: typeof Immutable,
refs?: (new (data: any) => unknown)[] | null,
customReplacer?: (
key: string,
value: unknown,
defaultReplacer: (key: string, value: unknown) => unknown
) => unknown,
customReviver?: (
key: string,
value: unknown,
defaultReviver: (key: string, value: unknown) => unknown
) => unknown
) {
return {
stringify: function (data: unknown) {
return jsan.stringify(
data,
serialize(immutable, refs, customReplacer, customReviver).replacer,
undefined,
options
);
},
parse: function (data: string) {
return jsan.parse(
data,
serialize(immutable, refs, customReplacer, customReviver).reviver
);
},
serialize: serialize,
};
}

View File

@ -0,0 +1,105 @@
import Immutable, { OrderedSet, Record } from 'immutable';
import { mark, extract, refer } from '../helpers';
import options from '../constants/options';
import { SerializedImmutableData } from '../types';
export default function serialize(
immutable: typeof Immutable,
refs?: (new (data: any) => unknown)[] | null,
customReplacer?: (
key: string,
value: unknown,
defaultReplacer: (key: string, value: unknown) => unknown
) => unknown,
customReviver?: (
key: string,
value: unknown,
defaultReviver: (key: string, value: unknown) => unknown
) => unknown
) {
function replacer(key: string, value: unknown) {
if (value instanceof immutable.Record)
return refer(value as Record<any>, 'ImmutableRecord', 'toObject', refs);
if (value instanceof immutable.Range)
return extract(value, 'ImmutableRange');
if (value instanceof immutable.Repeat)
return extract(value, 'ImmutableRepeat');
if (immutable.OrderedMap.isOrderedMap(value))
return mark(value, 'ImmutableOrderedMap', 'toObject');
if (immutable.Map.isMap(value))
return mark(value, 'ImmutableMap', 'toObject');
if (immutable.List.isList(value))
return mark(value, 'ImmutableList', 'toArray');
if (immutable.OrderedSet.isOrderedSet(value))
return mark(
value as OrderedSet<unknown>,
'ImmutableOrderedSet',
'toArray'
);
if (immutable.Set.isSet(value))
return mark(value, 'ImmutableSet', 'toArray');
if (immutable.Seq.isSeq(value))
return mark(value, 'ImmutableSeq', 'toArray');
if (immutable.Stack.isStack(value))
return mark(value, 'ImmutableStack', 'toArray');
return value;
}
function reviver(key: string, value: unknown) {
if (
typeof value === 'object' &&
value !== null &&
'__serializedType__' in value
) {
const immutableValue = value as SerializedImmutableData;
switch (immutableValue.__serializedType__) {
case 'ImmutableMap':
return immutable.Map(immutableValue.data);
case 'ImmutableOrderedMap':
return immutable.OrderedMap(immutableValue.data);
case 'ImmutableList':
return immutable.List(immutableValue.data);
case 'ImmutableRange':
return immutable.Range(
immutableValue.data._start,
immutableValue.data._end,
immutableValue.data._step
);
case 'ImmutableRepeat':
return immutable.Repeat(
immutableValue.data._value,
immutableValue.data.size
);
case 'ImmutableSet':
return immutable.Set(immutableValue.data);
case 'ImmutableOrderedSet':
return immutable.OrderedSet(immutableValue.data);
case 'ImmutableSeq':
return immutable.Seq(immutableValue.data);
case 'ImmutableStack':
return immutable.Stack(immutableValue.data);
case 'ImmutableRecord':
return refs && refs[immutableValue.__serializedRef__!]
? new refs[immutableValue.__serializedRef__!](immutableValue.data)
: immutable.Map(immutableValue.data);
default:
return (immutableValue as { data: unknown }).data;
}
}
return value;
}
return {
replacer: customReplacer
? function (key: string, value: unknown) {
return customReplacer(key, value, replacer);
}
: replacer,
reviver: customReviver
? function (key: string, value: unknown) {
return customReviver(key, value, reviver);
}
: reviver,
options: options,
};
}

View File

@ -1,4 +1,4 @@
var immutable = require('./immutable');
import immutable from './immutable';
module.exports = {
immutable: immutable,

View File

@ -0,0 +1,30 @@
declare module 'jsan' {
export interface Options {
date?: boolean;
function?: boolean;
regex?: boolean;
undefined?: boolean;
error?: boolean;
symbol?: boolean;
map?: boolean;
set?: boolean;
nan?: boolean;
infinity?: boolean;
refs?: boolean;
}
export function stringify(
value: unknown,
replacer?: (
key: string,
value: unknown
) => unknown | (number | string)[] | null,
space?: string | number,
_options?: Options | boolean
): string;
export function parse(
text: string,
reviver?: (key: string, value: unknown) => unknown
): unknown;
}

View File

@ -0,0 +1,71 @@
interface SerializedImmutableMap {
data: Record<string, unknown>;
__serializedType__: 'ImmutableMap';
}
interface SerializedImmutableOrderedMap {
data: Record<string, unknown>;
__serializedType__: 'ImmutableOrderedMap';
}
interface SerializedImmutableList {
data: unknown[];
__serializedType__: 'ImmutableList';
}
interface SerializedImmutableRangeData {
_start: number | undefined;
_end: number | undefined;
_step: number | undefined;
}
interface SerializedImmutableRange {
data: SerializedImmutableRangeData;
__serializedType__: 'ImmutableRange';
}
interface SerializedImmutableRepeatData {
_value: unknown;
size: number | undefined;
}
interface SerializedImmutableRepeat {
data: SerializedImmutableRepeatData;
__serializedType__: 'ImmutableRepeat';
}
interface SerializedImmutableSet {
data: unknown[];
__serializedType__: 'ImmutableSet';
}
interface SerializedImmutableOrderedSet {
data: unknown[];
__serializedType__: 'ImmutableOrderedSet';
}
interface SerializedImmutableSeq {
data: unknown[];
__serializedType__: 'ImmutableSeq';
}
interface SerializedImmutableStack {
data: unknown[];
__serializedType__: 'ImmutableStack';
}
interface SerializedImmutableRecord {
data: Record<string, unknown>;
__serializedType__: 'ImmutableRecord';
__serializedRef__?: number;
}
export type SerializedImmutableData =
| SerializedImmutableMap
| SerializedImmutableOrderedMap
| SerializedImmutableList
| SerializedImmutableRange
| SerializedImmutableRepeat
| SerializedImmutableSet
| SerializedImmutableOrderedSet
| SerializedImmutableSeq
| SerializedImmutableStack
| SerializedImmutableRecord;

View File

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Immutable Nested stringify 1`] = `"{\\"data\\":[[\\"map\\",{\\"data\\":{\\"seq\\":{\\"data\\":[1,2,3,4,5,6,7,8],\\"__serializedType__\\":\\"ImmutableSeq\\"},\\"stack\\":{\\"data\\":[\\"a\\",\\"b\\",\\"c\\"],\\"__serializedType__\\":\\"ImmutableStack\\"}},\\"__serializedType__\\":\\"ImmutableOrderedMap\\"}],[\\"repeat\\",{\\"data\\":{\\"_value\\":\\"hi\\",\\"size\\":100},\\"__serializedType__\\":\\"ImmutableRepeat\\"}]],\\"__serializedType__\\":\\"ImmutableSet\\"}"`;
exports[`Immutable Nested stringify 1`] = `"{\\"data\\":[{\\"data\\":{\\"map\\":{\\"data\\":{\\"seq\\":{\\"data\\":[1,2,3,4,5,6,7,8],\\"__serializedType__\\":\\"ImmutableSeq\\"},\\"stack\\":{\\"data\\":[\\"a\\",\\"b\\",\\"c\\"],\\"__serializedType__\\":\\"ImmutableStack\\"}},\\"__serializedType__\\":\\"ImmutableOrderedMap\\"},\\"repeat\\":{\\"data\\":{\\"_value\\":\\"hi\\",\\"size\\":100},\\"__serializedType__\\":\\"ImmutableRepeat\\"}},\\"__serializedType__\\":\\"ImmutableRecord\\",\\"__serializedRef__\\":0},{\\"data\\":[10,9,8,7,6,5,4,3,2,1],\\"__serializedType__\\":\\"ImmutableOrderedSet\\"},{\\"data\\":{\\"_start\\":0,\\"_end\\":7,\\"_step\\":1,\\"size\\":7},\\"__serializedType__\\":\\"ImmutableRange\\"}],\\"__serializedType__\\":\\"ImmutableSet\\"}"`;
exports[`Immutable Record stringify 1`] = `"{\\"data\\":{\\"a\\":1,\\"b\\":3},\\"__serializedType__\\":\\"ImmutableRecord\\",\\"__serializedRef__\\":0}"`;

View File

@ -1,7 +1,4 @@
var helpers = require('../helpers');
var mark = helpers.mark;
var extract = helpers.extract;
var refer = helpers.refer;
import { mark, extract, refer } from '../src/helpers';
describe('Helpers', function () {
it('mark', function () {
@ -16,12 +13,12 @@ describe('Helpers', function () {
});
it('refer', function () {
var TestClass = function (data) {
const TestClass = function (data: unknown) {
return data;
};
var testInstance = new TestClass({ testData: 'test' });
const testInstance = new (TestClass as any)({ testData: 'test' });
expect(
refer(testInstance, 'testType', false, [TestClass])
refer(testInstance, 'testType', false, [TestClass as any])
).toMatchSnapshot();
});
});

View File

@ -1,148 +0,0 @@
var Immutable = require('immutable');
var Serialize = require('../immutable');
var serialize = Serialize(Immutable);
var stringify = serialize.stringify;
var parse = serialize.parse;
var data = {
map: Immutable.Map({ a: 1, b: 2, c: 3, d: 4 }),
orderedMap: Immutable.OrderedMap({ b: 2, a: 1, c: 3, d: 4 }),
list: Immutable.List([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
range: Immutable.Range(0, 7),
repeat: Immutable.Repeat('hi', 100),
set: Immutable.Set([10, 9, 8, 7, 6, 5, 4, 3, 2, 1]),
orderedSet: Immutable.OrderedSet([10, 9, 8, 7, 6, 5, 4, 3, 2, 1]),
seq: Immutable.Seq([1, 2, 3, 4, 5, 6, 7, 8]),
stack: Immutable.Stack.of('a', 'b', 'c'),
};
describe('Immutable', function () {
var stringified = {};
describe('Stringify', function () {
Object.keys(data).forEach(function (key) {
it(key, function () {
stringified[key] = stringify(data[key]);
expect(stringified[key]).toMatchSnapshot();
});
});
});
describe('Parse', function () {
Object.keys(data).forEach(function (key) {
it(key, function () {
expect(parse(stringified[key])).toEqual(data[key]);
});
});
});
describe('Record', function () {
var ABRecord = Immutable.Record({ a: 1, b: 2 });
var myRecord = new ABRecord({ b: 3 });
var serialize = Serialize(Immutable, [ABRecord]);
var stringify = serialize.stringify;
var parse = serialize.parse;
var stringifiedRecord;
it('stringify', function () {
stringifiedRecord = stringify(myRecord);
expect(stringifiedRecord).toMatchSnapshot();
});
it('parse', function () {
expect(parse(stringifiedRecord)).toEqual(myRecord);
});
});
describe('Nested', function () {
var ABRecord = Immutable.Record({
map: Immutable.OrderedMap({ seq: data.seq, stack: data.stack }),
repeat: data.repeat,
});
var nestedData = Immutable.Set(ABRecord(), data.orderedSet, data.range);
var serialize = Serialize(Immutable, [ABRecord]);
var stringify = serialize.stringify;
var parse = serialize.parse;
var stringifiedNested;
it('stringify', function () {
stringifiedNested = stringify(nestedData);
expect(stringifiedNested).toMatchSnapshot();
});
it('parse', function () {
expect(parse(stringifiedNested)).toEqual(nestedData);
});
});
describe('With references', function () {
it('serializes and deserializes', function () {
var sharedValue = [];
var record = Immutable.Record({
prop: sharedValue,
});
var refs = [record];
var obj = Immutable.Map({
fst: new record(),
scnd: new record(),
});
var serialized = stringify(
obj,
Serialize(Immutable, refs).replacer,
null,
true
);
var parsed = JSON.parse(serialized);
var fstProp = parsed.data.fst.data.prop;
var scndProp = parsed.data.scnd.data.prop;
expect(fstProp).toEqual(scndProp);
expect(Array.isArray(obj.get('fst').get('prop')));
});
});
describe('Custom replacer and reviver functions', function () {
var customOneRepresentation = 'one';
function customReplacer(key, value, defaultReplacer) {
if (value === 1) {
return { data: customOneRepresentation, __serializedType__: 'number' };
}
return defaultReplacer(key, value);
}
function customReviver(key, value, defaultReviver) {
if (
typeof value === 'object' &&
value.__serializedType__ === 'number' &&
value.data === customOneRepresentation
) {
return 1;
}
return defaultReviver(key, value);
}
var serializeCustom = Serialize(
Immutable,
null,
customReplacer,
customReviver
);
Object.keys(data).forEach(function (key) {
var stringified = serializeCustom.stringify(data[key]);
it(key, function () {
var deserialized = serializeCustom.parse(stringified);
expect(deserialized).toEqual(data[key]);
if (key === 'map' || key === 'orderedMap') {
var deserializedDefault = parse(stringified);
expect(deserializedDefault.get('a')).toEqual(customOneRepresentation);
}
});
});
});
});

View File

@ -0,0 +1,159 @@
import Immutable, { Map, OrderedMap } from 'immutable';
import Serialize from '../src/immutable';
import { SerializedData } from '../src/helpers';
const serialize = Serialize(Immutable);
const stringify = serialize.stringify;
const parse = serialize.parse;
const data = {
map: Immutable.Map({ a: 1, b: 2, c: 3, d: 4 }),
orderedMap: Immutable.OrderedMap({ b: 2, a: 1, c: 3, d: 4 }),
list: Immutable.List([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
range: Immutable.Range(0, 7),
repeat: Immutable.Repeat('hi', 100),
set: Immutable.Set([10, 9, 8, 7, 6, 5, 4, 3, 2, 1]),
orderedSet: Immutable.OrderedSet([10, 9, 8, 7, 6, 5, 4, 3, 2, 1]),
seq: Immutable.Seq([1, 2, 3, 4, 5, 6, 7, 8]),
stack: Immutable.Stack.of('a', 'b', 'c'),
};
describe('Immutable', function () {
const stringified: { [key: string]: string } = {};
describe('Stringify', function () {
Object.keys(data).forEach(function (key) {
it(key, function () {
stringified[key] = stringify(data[key as keyof typeof data]);
expect(stringified[key]).toMatchSnapshot();
});
});
});
describe('Parse', function () {
Object.keys(data).forEach(function (key) {
it(key, function () {
expect(parse(stringified[key])).toEqual(data[key as keyof typeof data]);
});
});
});
describe('Record', function () {
const ABRecord = Immutable.Record({ a: 1, b: 2 });
const myRecord = new ABRecord({ b: 3 });
const serialize = Serialize(Immutable, [ABRecord]);
const stringify = serialize.stringify;
const parse = serialize.parse;
let stringifiedRecord: string;
it('stringify', function () {
stringifiedRecord = stringify(myRecord);
expect(stringifiedRecord).toMatchSnapshot();
});
it('parse', function () {
expect(parse(stringifiedRecord)).toEqual(myRecord);
});
});
describe('Nested', function () {
const ABRecord = Immutable.Record({
map: Immutable.OrderedMap({ seq: data.seq, stack: data.stack }),
repeat: data.repeat,
});
const nestedData = Immutable.Set([ABRecord(), data.orderedSet, data.range]);
const serialize = Serialize(Immutable, [ABRecord]);
const stringify = serialize.stringify;
const parse = serialize.parse;
let stringifiedNested: string;
it('stringify', function () {
stringifiedNested = stringify(nestedData);
expect(stringifiedNested).toMatchSnapshot();
});
it('parse', function () {
expect(parse(stringifiedNested)).toEqual(nestedData);
});
});
describe('With references', function () {
it('serializes and deserializes', function () {
const sharedValue: unknown[] = [];
const record = Immutable.Record({
prop: sharedValue,
});
const refs = [record];
const obj = Immutable.Map({
fst: new record(),
scnd: new record(),
});
const serialize = Serialize(Immutable, refs);
const serialized = serialize.stringify(obj);
const parsed = JSON.parse(serialized);
const fstProp = parsed.data.fst.data.prop;
const scndProp = parsed.data.scnd.data.prop;
expect(fstProp).toEqual(scndProp);
expect(Array.isArray(obj.get('fst')!.get('prop'))).toBe(true);
});
});
describe('Custom replacer and reviver functions', function () {
const customOneRepresentation = 'one';
function customReplacer(
key: string,
value: unknown,
defaultReplacer: (key: string, value: unknown) => unknown
) {
if (value === 1) {
return { data: customOneRepresentation, __serializedType__: 'number' };
}
return defaultReplacer(key, value);
}
function customReviver(
key: string,
value: unknown,
defaultReviver: (key: string, value: unknown) => unknown
) {
if (
typeof value === 'object' &&
(value as SerializedData).__serializedType__ === 'number' &&
(value as SerializedData).data === customOneRepresentation
) {
return 1;
}
return defaultReviver(key, value);
}
const serializeCustom = Serialize(
Immutable,
null,
customReplacer,
customReviver
);
Object.keys(data).forEach(function (key) {
const stringified = serializeCustom.stringify(
data[key as keyof typeof data]
);
it(key, function () {
const deserialized = serializeCustom.parse(stringified);
expect(deserialized).toEqual(data[key as keyof typeof data]);
if (key === 'map' || key === 'orderedMap') {
const deserializedDefault = parse(stringified);
expect(
(deserializedDefault as
| Map<unknown, unknown>
| OrderedMap<unknown, unknown>).get('a')
).toEqual(customOneRepresentation);
}
});
});
});
});

View File

@ -0,0 +1,4 @@
{
"extends": "../../../tsconfig.base.json",
"include": ["../src", "."]
}

View File

@ -0,0 +1,7 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "lib"
},
"include": ["src"]
}