diff --git a/lodash.js b/lodash.js index 1917b418c..b9a03adb2 100644 --- a/lodash.js +++ b/lodash.js @@ -26,7 +26,7 @@ /** Used as the internal argument placeholder. */ var PLACEHOLDER = '__lodash_placeholder__'; - /** Used to compose bitmasks for wrapper metadata. */ + /** Used to compose bitmasks for function metadata. */ var BIND_FLAG = 1, BIND_KEY_FLAG = 2, CURRY_BOUND_FLAG = 4, @@ -66,6 +66,19 @@ MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1, HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1; + /** Used to associate wrap methods with their bit flags. */ + var wrapFlags = [ + ['ary', ARY_FLAG], + ['bind', BIND_FLAG], + ['bindKey', BIND_KEY_FLAG], + ['curry', CURRY_FLAG], + ['curryRight', CURRY_RIGHT_FLAG], + ['flip', FLIP_FLAG], + ['partial', PARTIAL_FLAG], + ['partialRight', PARTIAL_RIGHT_FLAG], + ['rearg', REARG_FLAG] + ]; + /** `Object#toString` result references. */ var argsTag = '[object Arguments]', arrayTag = '[object Array]', @@ -130,6 +143,10 @@ reTrimStart = /^\s+/, reTrimEnd = /\s+$/; + /** Used to match wrap detail comments. */ + var reWrapComment = /\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/, + reWrapDetails = /\{\n\/\* \[wrapped with (.+)\] \*/; + /** Used to match non-compound words composed of alphanumeric characters. */ var reBasicWord = /[a-zA-Z0-9]+/g; @@ -1366,6 +1383,14 @@ WeakMap = getNative(context, 'WeakMap'), nativeCreate = getNative(context.Object, 'create'); + /* Used to set `toString` methods. */ + var defineProperty = (function() { + var func = getNative(context.Object, 'defineProperty'), + name = getNative.name; + + return (name && name.length > 2) ? func : undefined; + }()); + /** Used to store function metadata. */ var metaMap = WeakMap && new WeakMap; @@ -5040,7 +5065,7 @@ setData(result, newData); } result.placeholder = placeholder; - return result; + return setWrapToString(result, func, bitmask); } /** @@ -5177,7 +5202,7 @@ result = createHybrid.apply(undefined, newData); } var setter = data ? baseSetData : setData; - return setter(result, newData); + return setWrapToString(setter(result, newData), func, bitmask); } /** @@ -6130,6 +6155,41 @@ }; }()); + /** + * Sets the `toString` method of `wrapper` to mimic the source of `ref` + * with wrapper details added to a comment at the top of the source body. + * + * @private + * @param {Function} wrapper The function to modify. + * @param {Function} ref The reference function. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @returns {Function} Returns `wrapper`. + */ + var setWrapToString = !defineProperty ? identity : function(wrapper, ref, bitmask) { + var string = (ref + ''), + match = string.match(reWrapDetails), + details = match ? match[1].split(/, (?:& )| & /) : []; + + arrayEach(wrapFlags, function(pair) { + if ((bitmask & pair[1]) && !arrayIncludes(details, '_.'+ pair[0])) { + details.push('_.' + pair[0]); + } + }); + + var length = details.length, + lastIndex = length - 1; + + details[lastIndex] = (length > 1 ? '& ' : '') + details[lastIndex]; + details = details.join(length > 2 ? ', ' : ' '); + string = string.replace(reWrapComment, '{\n/* [wrapped with ' + details + '] */\n'); + + return defineProperty(wrapper, 'toString', { + 'configurable': true, + 'enumerable': false, + 'value': constant(string) + }); + }; + /** * Converts `string` to a property path array. *