Skip to content

Commit

Permalink
refactor(SymbolShim): add for polyfill
Browse files Browse the repository at this point in the history
- refactor SymbolShim away from class usage
- removes test that might be brittle when testing in browsers without Symbol impl
- adds for function polyfill if not present
- improves test looking for Set class
  • Loading branch information
benlesh committed Dec 8, 2015
1 parent dedbfc8 commit feaaff0
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 92 deletions.
97 changes: 48 additions & 49 deletions spec/util/SymbolShim-spec.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
/* globals __root__ */
var SymbolDefinition = require('../../dist/cjs/util/SymbolShim').SymbolDefinition;
var SymbolShim = require('../../dist/cjs/util/SymbolShim');
var Map = require('../../dist/cjs/util/Map').Map;
var Rx = require('../../dist/cjs/Rx');
var polyfillSymbol = SymbolShim.polyfillSymbol;
var ensureIterator = SymbolShim.ensureIterator;

describe('SymbolDefinition', function () {
describe('SymbolShim', function () {
it('should setup symbol if root does not have it', function () {
var root = {};

var result = new SymbolDefinition(root);
var result = polyfillSymbol(root);
expect(root.Symbol).toBeDefined();
expect(result.observable).toBeDefined();
expect(result.iterator).toBeDefined();
expect(result.for).toBeDefined();
});

it('should have observable, iterator symbols', function () {
var result = new SymbolDefinition(__root__);
it('should add a for method', function () {
var root = {};
var result = polyfillSymbol(root);
expect(typeof result.for).toBe('function');

expect(typeof result.observable).toBe('symbol');
expect(typeof result.iterator).toBe('symbol');
var test = result.for('test');
expect(test).toBe('@@test');
});

describe('when symbols exists on root', function () {
Expand All @@ -29,7 +34,7 @@ describe('SymbolDefinition', function () {
}
};

var result = new SymbolDefinition(root);
var result = polyfillSymbol(root);
expect(result.observable).toBe(root.Symbol.observable);
expect(result.iterator).toBe(root.Symbol.iterator);
});
Expand All @@ -43,60 +48,54 @@ describe('SymbolDefinition', function () {
}
};

var result = new SymbolDefinition(root);
var result = polyfillSymbol(root);
expect(result.observable).toBe(root.Symbol.for('observable'));
});

it('should patch root if for symbol does not exist', function () {
var root = {};

var result = new SymbolDefinition(root);
var result = polyfillSymbol(root);
expect(result.observable).toBe('@@observable');
});
});
});

describe('ensureIterator', function () {
it('should patch root using for symbol if exist', function () {
var root = {
Symbol: {
for: function (x) { return x; }
}
};
ensureIterator(root.Symbol, root);
expect(root.Symbol.iterator).toBe(root.Symbol.for('iterator'));
});

describe('iterator symbol', function () {
it('should patch root using for symbol if exist', function () {
var root = {
Symbol: {
for: function (x) { return x; }
}
};

var result = new SymbolDefinition(root);
expect(result.iterator).toBe(root.Symbol.for('iterator'));
});

it('should patch root if for symbol does not exist', function () {
var root = {};

var result = new SymbolDefinition(root);
expect(result.iterator).toBe('@@iterator');
});
it('should patch using Set for mozilla bug', function () {
function Set() {
}
Set.prototype['@@iterator'] = function () {};

it('should patch using set for mozilla', function () {
var root = {
Set: function () {
var ret = {};
ret['@@iterator'] = function () {};
return ret;
}
};
var root = {
Set: Set,
Symbol: {}
};

var result = new SymbolDefinition(root);
expect(result.iterator).toBe('@@iterator');
});
ensureIterator(root.Symbol, root);
expect(root.Symbol.iterator).toBe('@@iterator');
});

it('should patch using map for es6-shim', function () {
var root = {
Map: Map
};
it('should patch using map for es6-shim', function () {
var root = {
Map: Map,
Symbol: {}
};

root.Map.prototype.key = 'iteratorValue';
root.Map.prototype.entries = 'iteratorValue';
root.Map.prototype.key = 'iteratorValue';
root.Map.prototype.entries = 'iteratorValue';

var result = new SymbolDefinition(root);
expect(result.iterator).toBe('key');
});
ensureIterator(root.Symbol, root);
expect(root.Symbol.iterator).toBe('key');
});
});
});
85 changes: 42 additions & 43 deletions src/util/SymbolShim.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,56 @@
import {root} from './root';

export class SymbolDefinition {
observable: symbol = null;
iterator: symbol = null;

private applyObservable(): symbol {
const root = this.root;

if (!root.Symbol.observable) {
if (typeof root.Symbol.for === 'function') {
root.Symbol.observable = root.Symbol.for('observable');
} else {
root.Symbol.observable = '@@observable';
}
}
export function polyfillSymbol(root) {
const Symbol = ensureSymbol(root);
ensureIterator(Symbol, root);
ensureObservable(Symbol);
return Symbol;
}

return root.Symbol.observable;
export function ensureSymbol(root) {
if (!root.Symbol) {
root.Symbol = {
for: symbolForPolyfill
};
}
return root.Symbol;
}

private applyIterator(): symbol {
const root = this.root;
export function symbolForPolyfill(key) {
return '@@' + key;
}

if (!root.Symbol.iterator) {
if (typeof root.Symbol.for === 'function') {
root.Symbol.iterator = root.Symbol.for('iterator');
} else if (root.Set && typeof new root.Set()['@@iterator'] === 'function') {
// Bug for mozilla version
root.Symbol.iterator = '@@iterator';
} else if (root.Map) {
// es6-shim specific logic
let keys = Object.getOwnPropertyNames(root.Map.prototype);
for (let i = 0; i < keys.length; ++i) {
let key = keys[i];
if (key !== 'entries' && key !== 'size' && root.Map.prototype[key] === root.Map.prototype['entries']) {
root.Symbol.iterator = key;
break;
}
export function ensureIterator(Symbol, root) {
if (!Symbol.iterator) {
if (typeof Symbol.for === 'function') {
Symbol.iterator = Symbol.for('iterator');
} else if (root.Set && typeof new root.Set()['@@iterator'] === 'function') {
// Bug for mozilla version
Symbol.iterator = '@@iterator';
} else if (root.Map) {
// es6-shim specific logic
let keys = Object.getOwnPropertyNames(root.Map.prototype);
for (let i = 0; i < keys.length; ++i) {
let key = keys[i];
if (key !== 'entries' && key !== 'size' && root.Map.prototype[key] === root.Map.prototype['entries']) {
Symbol.iterator = key;
break;
}
} else {
root.Symbol.iterator = '@@iterator';
}
} else {
Symbol.iterator = '@@iterator';
}

return root.Symbol.iterator;
}
}

constructor(private root: any) {
if (!root.Symbol) {
root.Symbol = {};
export function ensureObservable(Symbol) {
if (!Symbol.observable) {
if (typeof Symbol.for === 'function') {
Symbol.observable = Symbol.for('observable');
} else {
Symbol.observable = '@@observable';
}

this.observable = this.applyObservable();
this.iterator = this.applyIterator();
}
}
export const SymbolShim = new SymbolDefinition(root);

export const SymbolShim = polyfillSymbol(root);

0 comments on commit feaaff0

Please sign in to comment.