diff --git a/.jscs.json b/.jscs.json index e83d9191..629794b7 100644 --- a/.jscs.json +++ b/.jscs.json @@ -135,6 +135,18 @@ "requireSpacesInGenerator": { "afterStar": true - } + }, + + "disallowSpacesInGenerator": { + "beforeStar": true + }, + + "disallowVar": false, + + "requireArrayDestructuring": false, + + "requireEnhancedObjectLiterals": false, + + "requireObjectDestructuring": false } diff --git a/.travis.yml b/.travis.yml index a96d78ca..95aa5798 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ language: node_js node_js: + - "5.1" - "5.0" - "4.2" - "4.1" @@ -39,6 +40,7 @@ sudo: false matrix: fast_finish: true allow_failures: + - node_js: "5.0" - node_js: "4.1" - node_js: "4.0" - node_js: "iojs-v3.2" diff --git a/es5-sham.js b/es5-sham.js index 9a15cbd8..8b46eeac 100644 --- a/es5-sham.js +++ b/es5-sham.js @@ -10,7 +10,7 @@ ; // UMD (Universal Module Definition) -// see https://github.com/umdjs/umd/blob/master/returnExports.js +// see https://github.com/umdjs/umd/blob/master/templates/returnExports.js (function (root, factory) { 'use strict'; diff --git a/es5-shim.js b/es5-shim.js index d6563dd0..143ae056 100644 --- a/es5-shim.js +++ b/es5-shim.js @@ -10,7 +10,7 @@ ; // UMD (Universal Module Definition) -// see https://github.com/umdjs/umd/blob/master/returnExports.js +// see https://github.com/umdjs/umd/blob/master/templates/returnExports.js (function (root, factory) { 'use strict'; @@ -56,6 +56,7 @@ var array_push = ArrayPrototype.push; var array_unshift = ArrayPrototype.unshift; var array_concat = ArrayPrototype.concat; var call = FunctionPrototype.call; +var apply = FunctionPrototype.apply; var max = Math.max; var min = Math.min; @@ -68,19 +69,18 @@ var isRegex; /* inlined from https://npmjs.com/is-regex */ var regexExec = RegEx var isString; /* inlined from https://npmjs.com/is-string */ var strValue = String.prototype.valueOf, tryStringObject = function tryStringObject(value) { try { strValue.call(value); return true; } catch (e) { return false; } }, stringClass = '[object String]'; isString = function isString(value) { if (typeof value === 'string') { return true; } if (typeof value !== 'object') { return false; } return hasToStringTag ? tryStringObject(value) : to_string.call(value) === stringClass; }; /* inlined from http://npmjs.com/define-properties */ +var supportsDescriptors = $Object.defineProperty && (function () { + try { + var obj = {}; + $Object.defineProperty(obj, 'x', { enumerable: false, value: obj }); + for (var _ in obj) { return false; } + return obj.x === obj; + } catch (e) { /* this is ES3 */ + return false; + } +}()); var defineProperties = (function (has) { - var supportsDescriptors = $Object.defineProperty && (function () { - try { - var obj = {}; - $Object.defineProperty(obj, 'x', { enumerable: false, value: obj }); - for (var _ in obj) { return false; } - return obj.x === obj; - } catch (e) { /* this is ES3 */ - return false; - } - }()); - - // Define configurable, writable and non-enumerable props + // Define configurable, writable, and non-enumerable props // if they don't exist. var defineProperty; if (supportsDescriptors) { @@ -323,10 +323,13 @@ defineProperties(FunctionPrototype, { // us it in defining shortcuts. var owns = call.bind(ObjectPrototype.hasOwnProperty); var toStr = call.bind(ObjectPrototype.toString); +var arraySlice = call.bind(array_slice); +var arraySliceApply = apply.bind(array_slice); var strSlice = call.bind(StringPrototype.slice); var strSplit = call.bind(StringPrototype.split); var strIndexOf = call.bind(StringPrototype.indexOf); var push = call.bind(array_push); +var isEnum = call.bind(ObjectPrototype.propertyIsEnumerable); // // Array @@ -733,7 +736,7 @@ defineProperties(ArrayPrototype, { var args = arguments; this.length = max(ES.ToInteger(this.length), 0); if (arguments.length > 0 && typeof deleteCount !== 'number') { - args = array_slice.call(arguments); + args = arraySlice(arguments); if (args.length < 2) { push(args, this.length - start); } else { @@ -782,7 +785,7 @@ defineProperties(ArrayPrototype, { k += 1; } - var items = array_slice.call(arguments, 2); + var items = arraySlice(arguments, 2); var itemCount = items.length; var to; if (itemCount < actualDeleteCount) { @@ -826,13 +829,26 @@ defineProperties(ArrayPrototype, { } }, !spliceWorksWithLargeSparseArrays || !spliceWorksWithSmallSparseArrays); -var hasJoinUndefinedBug = [1, 2].join(undefined) !== '1,2'; var originalJoin = ArrayPrototype.join; -defineProperties(ArrayPrototype, { - join: function join(separator) { - return originalJoin.call(this, typeof separator === 'undefined' ? ',' : separator); - } -}, hasJoinUndefinedBug); +var hasStringJoinBug = Array.prototype.join.call('123', ',') !== '1,2,3'; +if (hasStringJoinBug) { + defineProperties(ArrayPrototype, { + join: function join(separator) { + var sep = typeof separator === 'undefined' ? ',' : separator; + return originalJoin.call(isString(this) ? strSplit(this, '') : this, sep); + } + }, hasStringJoinBug); +} + +var hasJoinUndefinedBug = [1, 2].join(undefined) !== '1,2'; +if (hasJoinUndefinedBug) { + defineProperties(ArrayPrototype, { + join: function join(separator) { + var sep = typeof separator === 'undefined' ? ',' : separator; + return originalJoin.call(this, sep); + } + }, hasJoinUndefinedBug); +} var pushShim = function push(item) { var O = ES.ToObject(this); @@ -868,6 +884,16 @@ var pushUndefinedIsWeird = (function () { }()); defineProperties(ArrayPrototype, { push: pushShim }, pushUndefinedIsWeird); +// ES5 15.2.3.14 +// http://es5.github.io/#x15.4.4.10 +// Fix boxed string bug +defineProperties(ArrayPrototype, { + slice: function (start, end) { + var arr = isString(this) ? strSplit(this, '') : this; + return arraySliceApply(arr, arguments); + } +}, splitString); + // // Object // ====== @@ -893,7 +919,8 @@ var blacklistedKeys = { $frames: true, $frameElement: true, $webkitIndexedDB: true, - $webkitStorageInfo: true + $webkitStorageInfo: true, + $external: true }; var hasAutomationEqualityBug = (function () { /* globals window */ @@ -995,7 +1022,7 @@ var originalKeys = $Object.keys; defineProperties($Object, { keys: function keys(object) { if (isArguments(object)) { - return originalKeys(array_slice.call(object)); + return originalKeys(arraySlice(object)); } else { return originalKeys(object); } @@ -1051,8 +1078,8 @@ defineProperties(Date.prototype, { } // pad milliseconds to have three digits. return ( - year + '-' + array_slice.call(result, 0, 2).join('-') + - 'T' + array_slice.call(result, 2).join(':') + '.' + + year + '-' + arraySlice(result, 0, 2).join('-') + + 'T' + arraySlice(result, 2).join(':') + '.' + strSlice('000' + this.getUTCMilliseconds(), -3) + 'Z' ); } @@ -1123,7 +1150,6 @@ if (doesNotParseY2KNewYear || acceptsInvalidDates || !supportsExtendedYears) { /* global Date: true */ /* eslint-disable no-undef */ var maxSafeUnsigned32Bit = Math.pow(2, 31) - 1; - var secondsWithinMaxSafeUnsigned32Bit = Math.floor(maxSafeUnsigned32Bit / 1e3); var hasSafariSignedIntBug = isActualNaN(new Date(1970, 0, 1, 0, 0, 0, maxSafeUnsigned32Bit + 1).getTime()); Date = (function (NativeDate) { /* eslint-enable no-undef */ @@ -1496,7 +1522,7 @@ if ( var maxSafe32BitInt = Math.pow(2, 32) - 1; StringPrototype.split = function (separator, limit) { - var string = this; + var string = String(this); if (typeof separator === 'undefined' && limit === 0) { return []; } @@ -1515,7 +1541,6 @@ if ( // Make `global` and avoid `lastIndex` issues by working with a copy separator2, match, lastIndex, lastLength; var separatorCopy = new RegExp(separator.source, flags + 'g'); - string += ''; // Type-convert if (!compliantExecNpcg) { // Doesn't need flags gy, but they don't hurt separator2 = new RegExp('^' + separatorCopy.source + '$(?!\\s)', flags); @@ -1548,7 +1573,7 @@ if ( /* eslint-enable no-loop-func */ } if (match.length > 1 && match.index < string.length) { - array_push.apply(output, array_slice.call(match, 1)); + array_push.apply(output, arraySlice(match, 1)); } lastLength = match[0].length; lastLastIndex = lastIndex; @@ -1700,7 +1725,6 @@ if (parseInt(ws + '08') !== 8 || parseInt(ws + '0x16') !== 22) { } if (String(new RangeError('test')) !== 'RangeError: test') { - var originalErrorToString = Error.prototype.toString; var errorToStringShim = function toString() { if (typeof this === 'undefined' || this === null) { throw new TypeError("can't convert " + this + ' to object'); @@ -1729,4 +1753,19 @@ if (String(new RangeError('test')) !== 'RangeError: test') { Error.prototype.toString = errorToStringShim; } +if (supportsDescriptors) { + var ensureNonEnumerable = function (obj, prop) { + if (isEnum(obj, prop)) { + var desc = Object.getOwnPropertyDescriptor(obj, prop); + desc.enumerable = false; + Object.defineProperty(obj, prop, desc); + } + }; + ensureNonEnumerable(Error.prototype, 'message'); + if (Error.prototype.message !== '') { + Error.prototype.message = ''; + } + ensureNonEnumerable(Error.prototype, 'name'); +} + })); diff --git a/package.json b/package.json index 4c607ceb..7e3bca0a 100644 --- a/package.json +++ b/package.json @@ -33,13 +33,13 @@ "jscs": "jscs tests/helpers/*.js tests/spec/*.js es5-shim.js es5-sham.js" }, "devDependencies": { - "eslint": "^1.9.0", + "eslint": "^1.10.1", "@ljharb/eslint-config": "^1.6.0", "jasmine-node": "^1.14.5", - "jscs": "^2.5.1", - "uglify-js": "^2.6.0", + "jscs": "^2.6.0", + "uglify-js": "^2.6.1", "replace": "^0.3.0", - "semver": "^5.0.3" + "semver": "^5.1.0" }, "engines": { "node": ">=0.4.0" diff --git a/tests/spec/s-array.js b/tests/spec/s-array.js index 0b7c88ed..254c2e1c 100644 --- a/tests/spec/s-array.js +++ b/tests/spec/s-array.js @@ -1499,6 +1499,34 @@ describe('Array', function () { it('defaults to a comma separator when undefined is provided', function () { expect([1, 2].join(undefined)).toBe('1,2'); }); + + it('works, extended', function () { + expect([].join()).toBe(''); + expect([undefined].join()).toBe(''); + expect([undefined, undefined].join()).toBe(','); + expect([null, null].join()).toBe(','); + expect([undefined, undefined].join('|')).toBe('|'); + expect([null, null].join('|')).toBe('|'); + expect([1, 2, 3].join('|')).toBe('1|2|3'); + expect([1, 2, 3].join(null)).toBe('1null2null3'); + expect([1, 2, 3].join({})).toBe('1[object Object]2[object Object]3'); + expect([1, 2, 3].join('')).toBe('123'); + }); + + it('is generic', function () { + var obj = { 0: 1, 1: 2, 2: 3, 3: 4, length: 3 }; + expect(Array.prototype.join.call(obj, ',')).toBe('1,2,3'); + }); + + it('works with a string literal', function () { + var str = '123'; + expect(Array.prototype.join.call(str, ',')).toBe('1,2,3'); + }); + + it('works with `arguments`', function () { + var args = (function () { return arguments; }(1, 2, 3)); + expect(Array.prototype.join.call(args, ',')).toBe('1,2,3'); + }); }); describe('#push()', function () { @@ -1554,7 +1582,7 @@ describe('Array', function () { expect(result).toEqual([2, 3]); }); - it('works with arguments', function () { + it('works with `arguments`', function () { var obj = (function () { return arguments; }(1, 2, 3, 4)); diff --git a/tests/spec/s-error.js b/tests/spec/s-error.js index 1326408b..bd9917d0 100644 --- a/tests/spec/s-error.js +++ b/tests/spec/s-error.js @@ -1,8 +1,20 @@ -/* global describe, it, expect */ +/* global describe, it, xit, expect */ describe('Error', function () { 'use strict'; + var supportsDescriptors = Object.defineProperty && (function () { + try { + var obj = {}; + Object.defineProperty(obj, 'x', { enumerable: false, value: obj }); + for (var _ in obj) { return false; } + return obj.x === obj; + } catch (e) { /* this is ES3 */ + return false; + } + }()); + var ifSupportsDescriptorsIt = supportsDescriptors ? it : xit; + describe('#toString()', function () { it('stringifies a newed error properly', function () { var msg = 'test'; @@ -19,10 +31,32 @@ describe('Error', function () { throw new RangeError(msg); } catch (e) { error = e; - }; + } expect(error.name).toBe('RangeError'); expect(error.message).toBe(msg); expect(String(error)).toBe(error.name + ': ' + msg); }); }); + + describe('enumerability of prototype properties', function () { + ifSupportsDescriptorsIt('#message', function () { + var desc = Object.getOwnPropertyDescriptor(Error.prototype, 'message'); + expect(desc).toEqual({ + enumerable: false, + writable: true, + configurable: true, + value: '' + }); + }); + + ifSupportsDescriptorsIt('#name', function () { + var desc = Object.getOwnPropertyDescriptor(Error.prototype, 'name'); + expect(desc).toEqual({ + enumerable: false, + writable: true, + configurable: true, + value: 'Error' + }); + }); + }); }); diff --git a/tests/spec/s-object.js b/tests/spec/s-object.js index c17f1bde..7bd4ea0d 100644 --- a/tests/spec/s-object.js +++ b/tests/spec/s-object.js @@ -1,5 +1,15 @@ /* global describe, it, xit, expect, beforeEach, jasmine, window */ +var supportsDescriptors = Object.defineProperty && (function () { + try { + var obj = {}; + Object.defineProperty(obj, 'x', { enumerable: false, value: obj }); + for (var _ in obj) { return false; } + return obj.x === obj; + } catch (e) { /* this is ES3 */ + return false; + } +}()); var ifWindowIt = typeof window === 'undefined' ? xit : it; var extensionsPreventible = typeof Object.preventExtensions === 'function' && (function () { var obj = {}; @@ -130,8 +140,8 @@ describe('Object', function () { ifWindowIt('can serialize all objects on the `window`', function () { var has = Object.prototype.hasOwnProperty; var windowItemKeys, exception; - var blacklistedKeys = ['window', 'console', 'parent', 'self', 'frame', 'frames', 'frameElement']; - if (Object.defineProperty) { + var blacklistedKeys = ['window', 'console', 'parent', 'self', 'frame', 'frames', 'frameElement', 'external']; + if (supportsDescriptors) { Object.defineProperty(window, 'thrower', { configurable: true, get: function () { throw new RangeError('thrower!'); } }); } for (var k in window) { @@ -146,7 +156,9 @@ describe('Object', function () { expect(exception).toBeUndefined(); } } - delete window.thrower; + if (supportsDescriptors) { + delete window.thrower; + } }); });