From 91326d9c39f6357c9b6679906a90ee684e5c33f9 Mon Sep 17 00:00:00 2001 From: Gareth Tan Date: Sun, 16 Aug 2015 00:38:42 -0400 Subject: [PATCH 1/4] Add support for Immutable.js objects. --- agent/Agent.js | 7 +++++- agent/Bridge.js | 13 ++++++++++- agent/dehydrate.js | 10 ++++++-- agent/immutableUtils.js | 51 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 agent/immutableUtils.js diff --git a/agent/Agent.js b/agent/Agent.js index 778e8ce063..aa395cf0ee 100644 --- a/agent/Agent.js +++ b/agent/Agent.js @@ -12,6 +12,7 @@ var {EventEmitter} = require('events'); var assign = require('object-assign'); +var immutableUtils = require('./immutableUtils'); import type {RendererID, DataType, OpaqueNodeHandle, NativeType, Helpers} from '../backend/types'; @@ -378,7 +379,11 @@ function randid() { function getIn(base, path) { return path.reduce((obj, attr) => { - return obj ? obj[attr] : null; + var alt; + if (immutableUtils.isImmutable(obj)) { + alt = obj.get(attr); + } + return obj ? alt || obj[attr] : null; }, base); } diff --git a/agent/Bridge.js b/agent/Bridge.js index b80844402e..c63ff9b06d 100644 --- a/agent/Bridge.js +++ b/agent/Bridge.js @@ -13,6 +13,7 @@ var consts = require('./consts'); var hydrate = require('./hydrate'); var dehydrate = require('./dehydrate'); +var immutableUtils = require('../agent/immutableUtils'); type AnyFn = (...x: any) => any; export type Wall = { @@ -103,6 +104,7 @@ type PayloadType = { * determined that an object is no longer needed, call `.forget(id)` to clean * up. */ + class Bridge { _buffer: Array<{evt: string, data: any}>; _cbs: Map; @@ -318,6 +320,11 @@ class Bridge { if (val) { var protod = false; var isFn = typeof val === 'function'; + + if (immutableUtils.isImmutable(val)) { + val = immutableUtils.shallowToJS(val); + } + Object.getOwnPropertyNames(val).forEach(name => { if (name === '__proto__') { protod = true; @@ -354,7 +361,11 @@ class Bridge { function getIn(base, path) { return path.reduce((obj, attr) => { - return obj ? obj[attr] : null; + var alt; + if (immutableUtils.isImmutable(obj)) { + alt = obj.get(attr); + } + return obj ? alt || obj[attr] : null; }, base); } diff --git a/agent/dehydrate.js b/agent/dehydrate.js index 209f5719c5..7f9cf99261 100644 --- a/agent/dehydrate.js +++ b/agent/dehydrate.js @@ -30,6 +30,9 @@ * } * and cleaned = [["some", "attr"], ["other"]] */ + +var immutableUtils = require('./immutableUtils'); + function dehydrate(data: Object, cleaned: Array>, path?: Array, level?: number): string | Object { level = level || 0; path = path || []; @@ -77,9 +80,12 @@ function dehydrate(data: Object, cleaned: Array>, path?: Array Date: Sun, 16 Aug 2015 02:00:24 -0400 Subject: [PATCH 2/4] Add tests and fixes. --- agent/__tests__/dehydrate-test.js | 27 +++++++- agent/__tests__/immutableUtils-test.js | 86 ++++++++++++++++++++++++++ agent/dehydrate.js | 10 +-- agent/immutableUtils.js | 3 +- 4 files changed, 119 insertions(+), 7 deletions(-) create mode 100644 agent/__tests__/immutableUtils-test.js diff --git a/agent/__tests__/dehydrate-test.js b/agent/__tests__/dehydrate-test.js index 37690400d4..73d12d0227 100644 --- a/agent/__tests__/dehydrate-test.js +++ b/agent/__tests__/dehydrate-test.js @@ -10,7 +10,10 @@ 'use strict'; jest.dontMock('../dehydrate'); -var dehydrate = require('../dehydrate'); +jest.dontMock('../immutableUtils'); +jest.dontMock('immutable'); +var dehydrate = require('../dehydrate'), + Immutable = require('immutable'); describe('dehydrate', () => { it('leaves an empty object alone', () => { @@ -55,4 +58,26 @@ describe('dehydrate', () => { expect(result.a.b.c).toEqual({type: 'array', name: 'Array', meta: {length: 2}}); expect(result.a.b.d).toEqual({type: 'object', name: 'Something', meta: null}); }); + + it('cleans a shallowly nested Immutable objects with the appropriate name', function() { + var object = {c: Immutable.List([1, 3]), d: Immutable.Map(), e: Immutable.Set()}; + var cleaned = []; + var result = dehydrate(object, cleaned); + + expect(cleaned).toEqual([['c'], ['d'], ['e']]); + expect(result.c).toEqual({type: 'object', name: 'Immutable.List'}); + expect(result.d).toEqual({type: 'object', name: 'Immutable.Map'}); + expect(result.e).toEqual({type: 'object', name: 'Immutable.Set'}); + }); + + it('cleans a deeply nested Immutable objects with the appropriate name', function() { + var object = {a: {b: {c: Immutable.List([1, 3]), d: Immutable.Map(), e: Immutable.Set()}}}; + var cleaned = []; + var result = dehydrate(object, cleaned); + + expect(cleaned).toEqual([['a', 'b', 'c'], ['a', 'b', 'd'], ['a', 'b', 'e']]); + expect(result.a.b.c).toEqual({type: 'object', name: 'Immutable.List', meta: null}); + expect(result.a.b.d).toEqual({type: 'object', name: 'Immutable.Map', meta: null}); + expect(result.a.b.e).toEqual({type: 'object', name: 'Immutable.Set', meta: null}); + }); }); diff --git a/agent/__tests__/immutableUtils-test.js b/agent/__tests__/immutableUtils-test.js new file mode 100644 index 0000000000..356e683d6e --- /dev/null +++ b/agent/__tests__/immutableUtils-test.js @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. a additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +'use strict'; + +jest.dontMock('../immutableUtils'); +jest.dontMock('immutable'); +var immutableUtils = require('../immutableUtils'), + Immutable = require('immutable'); + +describe('immutableUtils', () => { + + describe('getImmutableName()', function() { + it('generates the right name for an OrderedMap', () => { + var name = immutableUtils.getImmutableName(Immutable.OrderedMap()); + expect(name).toBe('Immutable.OrderedMap'); + }); + + it('generates the right name for an OrderedSet', () => { + var name = immutableUtils.getImmutableName(Immutable.OrderedSet()); + expect(name).toBe('Immutable.OrderedSet'); + }); + + it('generates the right name for a Map', () => { + var name = immutableUtils.getImmutableName(Immutable.Map()); + expect(name).toBe('Immutable.Map'); + }); + + it('generates the right name for a Set', () => { + var name = immutableUtils.getImmutableName(Immutable.Set()); + expect(name).toBe('Immutable.Set'); + }); + + it('generates the right name for a List', () => { + var name = immutableUtils.getImmutableName(Immutable.List()); + expect(name).toBe('Immutable.List'); + }); + + it('generates the right name for a Stack', () => { + var name = immutableUtils.getImmutableName(Immutable.Stack()); + expect(name).toBe('Immutable.Stack'); + }); + + it('generates the right name for a Seq', () => { + var name = immutableUtils.getImmutableName(Immutable.Seq()); + expect(name).toBe('Immutable.Seq'); + }); + }); + + describe('shallowToJS()', function() { + it('converts a Map correctly', function() { + var map = Immutable.Map({ + a: 1, + b: 2, + c: Immutable.Map() + }); + var result = immutableUtils.shallowToJS(map); + expect(result.a).toBe(1); + expect(result.b).toBe(2); + expect(Immutable.Map.isMap(result.c)).toBe(true); + }); + + it('converts a List correctly', function() { + var list = Immutable.List(['a', 'b', Immutable.Map()]); + var result = immutableUtils.shallowToJS(list); + expect(result[0]).toBe('a'); + expect(result[1]).toBe('b'); + expect(Immutable.Map.isMap(result[2])).toBe(true); + }); + + it('converts a Set correctly', function() { + var set = Immutable.Set([1, 2, 3, Immutable.List()]); + var result = immutableUtils.shallowToJS(set); + + expect(Array.isArray(result)).toBe(true); + expect(result.filter(Immutable.List.isList).length > 0).toBe(true); + }); + }); + +}); diff --git a/agent/dehydrate.js b/agent/dehydrate.js index 7f9cf99261..39bb232b78 100644 --- a/agent/dehydrate.js +++ b/agent/dehydrate.js @@ -63,11 +63,16 @@ function dehydrate(data: Object, cleaned: Array>, path?: Array 2) { cleaned.push(path); return { type: Array.isArray(data) ? 'array' : 'object', - name: data.constructor.name === 'Object' ? '' : data.constructor.name, + name: data.constructor.name === 'Object' ? '' : name, meta: Array.isArray(data) ? { length: data.length, } : null, @@ -80,9 +85,6 @@ function dehydrate(data: Object, cleaned: Array>, path?: Array Date: Sun, 16 Aug 2015 02:17:55 -0400 Subject: [PATCH 3/4] Move to jest 0.5.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 59bea2bc8d..dbf1019928 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "babel-loader": "^5.1.4", "eslint": "^0.24.1", "eslint-plugin-react": "^3.0.0", - "jest-cli": "facebook/jest#0.5.x", + "jest-cli": "0.5.0", "json-loader": "^0.5.2", "node-libs-browser": "^0.5.2", "webpack": "^1.9.10" From 56040325ec26250e72c4cb26232b4d3d91db6b69 Mon Sep 17 00:00:00 2001 From: Gareth Tan Date: Sun, 16 Aug 2015 12:57:26 -0400 Subject: [PATCH 4/4] Fix flow errors. --- agent/Agent.js | 2 +- agent/Bridge.js | 2 +- agent/immutableUtils.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/agent/Agent.js b/agent/Agent.js index aa395cf0ee..127691097a 100644 --- a/agent/Agent.js +++ b/agent/Agent.js @@ -380,7 +380,7 @@ function randid() { function getIn(base, path) { return path.reduce((obj, attr) => { var alt; - if (immutableUtils.isImmutable(obj)) { + if (obj !== null && immutableUtils.isImmutable(obj) && obj.get) { alt = obj.get(attr); } return obj ? alt || obj[attr] : null; diff --git a/agent/Bridge.js b/agent/Bridge.js index c63ff9b06d..3124b67606 100644 --- a/agent/Bridge.js +++ b/agent/Bridge.js @@ -362,7 +362,7 @@ class Bridge { function getIn(base, path) { return path.reduce((obj, attr) => { var alt; - if (immutableUtils.isImmutable(obj)) { + if (obj !== null && immutableUtils.isImmutable(obj) && obj.get) { alt = obj.get(attr); } return obj ? alt || obj[attr] : null; diff --git a/agent/immutableUtils.js b/agent/immutableUtils.js index a2f7737c1d..57700e794a 100644 --- a/agent/immutableUtils.js +++ b/agent/immutableUtils.js @@ -18,7 +18,7 @@ var types = ['OrderedMap', 'OrderedSet', module.exports = { isImmutable: Immutable.Iterable.isIterable, - getImmutableName(data) { + getImmutableName(data: any): string { if (Immutable.Iterable.isIterable(data)) { var name = 'Immutable.', typelen = types.length; @@ -33,7 +33,7 @@ module.exports = { return data.constructor.name; } }, - shallowToJS(dataStructure) { + shallowToJS(dataStructure: any): any { if (!Immutable.Iterable.isIterable(dataStructure)) { return dataStructure; }