diff --git a/README.md b/README.md
index 9c93a10ee..0bd88e40c 100644
--- a/README.md
+++ b/README.md
@@ -58,7 +58,7 @@ For more information check out these screencasts over Lo-Dash:
## Support
-Lo-Dash has been tested in at least Chrome 5~24, Firefox 1~18, IE 6-10, Opera 9.25-12, Safari 3-6, Node.js 0.4.8-0.8.17, Narwhal 0.3.2, RingoJS 0.8, and Rhino 1.7RC5.
+Lo-Dash has been tested in at least Chrome 5~24, Firefox 1~18, IE 6-10, Opera 9.25-12, Safari 3-6, Node.js 0.4.8-0.8.18, Narwhal 0.3.2, RingoJS 0.8, and Rhino 1.7RC5.
## Custom builds
@@ -71,17 +71,22 @@ lodash backbone
```
* CSP builds, supporting default [Content Security Policy](http://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html) restrictions, may be created using the `csp` modifier argument.
- The `csp` modifier is an alais of the `mobile` modifier. Chrome extensions will require [sandboxing](http://developer.chrome.com/extensions/sandboxingEval.html) or the use of either the `csp`, `mobile`, or `underscore` build.
+ The `csp` modifier is an alais of the `modern` modifier. Chrome extensions will require [sandboxing](http://developer.chrome.com/extensions/sandboxingEval.html) or the use of either the `mobile`, `modern`, or `underscore` build.
```bash
lodash csp
```
- * Legacy builds, tailored for older browsers without [ES5 support](http://es5.github.com/), may be created using the `legacy` modifier argument.
+ * Legacy builds, tailored for older environments without [ES5 support](http://es5.github.com/), may be created using the `legacy` modifier argument.
```bash
lodash legacy
```
- * Mobile builds, with IE < 9 bug fixes and method compilation removed, may be created using the `mobile` modifier argument.
+ * Modern builds, tailored for newer environments with ES5 support, may be created using the `modern` modifier argument.
+```bash
+lodash modern
+```
+
+ * Mobile builds, without method compilation and bug fixes for old browsers, may be created using the `mobile` modifier argument.
```bash
lodash mobile
```
@@ -150,7 +155,7 @@ lodash settings="{interpolate:/\{\{([\s\S]+?)\}\}/g}"
lodash moduleId="underscore"
```
-All arguments, except `legacy` with `csp` or `mobile`, may be combined.
+All arguments, except `legacy` with `csp`, `mobile`, `modern`, or `underscore`, may be combined.
Unless specified by `-o` or `--output`, all files created are saved to the current working directory.
The following options are also supported:
diff --git a/build.js b/build.js
index 520f306b9..f6d101c59 100755
--- a/build.js
+++ b/build.js
@@ -563,8 +563,9 @@
'',
' lodash backbone Build with only methods required by Backbone',
' lodash csp Build supporting default Content Security Policy restrictions',
- ' lodash legacy Build tailored for older browsers without ES5 support',
- ' lodash mobile Build with IE < 9 bug fixes & method compilation removed',
+ ' lodash legacy Build tailored for older environments without ES5 support',
+ ' lodash modern Build tailored for newer environments with ES5 support',
+ ' lodash mobile Build without method compilation and bug fixes for old browsers',
' lodash strict Build with `_.assign`, `_.bindAll`, & `_.defaults` in strict mode',
' lodash underscore Build tailored for projects already using Underscore',
' lodash include=... Comma separated method/category names to include in the build',
@@ -583,7 +584,7 @@
' (e.g. `lodash settings="{interpolate:/\\{\\{([\\s\\S]+?)\\}\\}/g}"`)',
' lodash moduleId=... The AMD module ID of Lo-Dash, which defaults to “lodash”, used by precompiled templates',
'',
- ' All arguments, except `legacy` with `csp` or `mobile`, may be combined.',
+ ' All arguments, except `legacy` with `csp`, `mobile`, `modern`, or `underscore`, may be combined.',
' Unless specified by `-o` or `--output`, all files created are saved to the current working directory.',
'',
' Options:',
@@ -854,33 +855,47 @@
}
/**
- * Removes the `createFunction` function from `source`.
+ * Removes all `argsAreObjects` references from `source`.
*
* @private
* @param {String} source The source to process.
* @returns {String} Returns the modified source.
*/
- function removeCreateFunction(source) {
- source = removeVar(source, 'isFirefox');
- return removeFunction(source, 'createFunction')
- .replace(/createFunction/g, 'Function')
- .replace(/(?:\s*\/\/.*)*\s*if *\(isIeOpera[^}]+}\n/, '');
+ function removeArgsAreObjects(source) {
+ source = removeVar(source, 'argsAreObjects');
+
+ // remove `argsAreObjects` from `_.isArray`
+ source = source.replace(matchFunction(source, 'isArray'), function(match) {
+ return match.replace(/\(argsAreObjects && *([^)]+)\)/g, '$1');
+ });
+
+ // remove `argsAreObjects` from `_.isEqual`
+ source = source.replace(matchFunction(source, 'isEqual'), function(match) {
+ return match.replace(/!argsAreObjects[^:]+:\s*/g, '');
+ });
+
+ return source;
}
/**
- * Removes the all references to `refName` from `createIterator` in `source`.
+ * Removes the all references to `varName` from `createIterator` in `source`.
*
* @private
* @param {String} source The source to process.
- * @param {String} refName The name of the reference to remove.
+ * @param {String} varName The name of the variable to remove.
* @returns {String} Returns the modified source.
*/
- function removeFromCreateIterator(source, refName) {
- var snippet = matchFunction(source, 'createIterator');
- if (snippet) {
+ function removeFromCreateIterator(source, varName) {
+ var snippet = matchFunction(source, 'createIterator');
+ if ( snippet) {
+ // remove data object property assignment
+ var modified = snippet.replace(RegExp("^ *'" + varName + "': *" + varName + '.+\\n', 'm'), '');
+ source = source.replace(snippet, modified);
+
// clip the snippet at the `factory` assignment
- snippet = snippet.match(/Function\([\s\S]+$/)[0];
- var modified = snippet.replace(RegExp('\\b' + refName + '\\b,? *', 'g'), '');
+ snippet = modified.match(/Function\([\s\S]+$/)[0];
+ modified = snippet.replace(RegExp('\\b' + varName + '\\b,? *', 'g'), '');
+
source = source.replace(snippet, modified);
}
return source;
@@ -915,6 +930,59 @@
return removeFromCreateIterator(source, funcName);
}
+ /**
+ * Removes all `hasDontEnumBug` references from `source`.
+ *
+ * @private
+ * @param {String} source The source to process.
+ * @returns {String} Returns the modified source.
+ */
+ function removeHasDontEnumBug(source) {
+ source = removeFromCreateIterator(source, 'hasDontEnumBug');
+ source = removeFromCreateIterator(source, 'shadowed');
+
+ // remove `hasDontEnumBug` declaration and assignment
+ source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var hasDontEnumBug;|.+?hasDontEnumBug *=.+/g, '');
+
+ // remove `shadowed` variable
+ source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var shadowed[\s\S]+?;\n/, '');
+
+ // remove JScript `[[DontEnum]]` fix from `iteratorTemplate`
+ source = source.replace(getIteratorTemplate(source), function(match) {
+ return match.replace(/(?: *\/\/.*\n)* *["']( *)<% *if *\(hasDontEnumBug[\s\S]+?["']\1<% *} *%>.+/, '');
+ });
+
+ return source;
+ }
+
+ /**
+ * Removes all `hasEnumPrototype` references from `source`.
+ *
+ * @private
+ * @param {String} source The source to process.
+ * @returns {String} Returns the modified source.
+ */
+ function removeHasEnumPrototype(source) {
+ source = removeFromCreateIterator(source, 'hasEnumPrototype');
+
+ // remove `hasEnumPrototype` declaration and assignment
+ source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var hasEnumPrototype;|.+?hasEnumPrototype *=.+/g, '');
+
+ // remove `prototype` [[Enumerable]] fix from `_.keys`
+ source = source.replace(matchFunction(source, 'keys'), function(match) {
+ return match.replace(/(?:\s*\/\/.*)*(\s*return *).+?propertyIsEnumerable[\s\S]+?: */, '$1');
+ });
+
+ // remove `prototype` [[Enumerable]] fix from `iteratorTemplate`
+ source = source.replace(getIteratorTemplate(source), function(match) {
+ return match
+ .replace(/(?: *\/\/.*\n)* *["'] *(?:<% *)?if *\(hasEnumPrototype *(?:&&|\))[\s\S]+?<% *} *(?:%>|["']).+/g, '')
+ .replace(/hasEnumPrototype *\|\|/g, '');
+ });
+
+ return source;
+ }
+
/**
* Removes the `_.isArguments` fallback from `source`.
*
@@ -937,6 +1005,25 @@
return source.replace(getIsFunctionFallback(source), '');
}
+ /**
+ * Removes all `iteratesOwnLast` references from `source`.
+ *
+ * @private
+ * @param {String} source The source to process.
+ * @returns {String} Returns the modified source.
+ */
+ function removeIteratesOwnLast(source) {
+ // remove `iteratesOwnLast` declaration and assignment
+ source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var iteratesOwnLast;|.+?iteratesOwnLast *=.+/g, '');
+
+ // remove `iteratesOwnLast` from `shimIsPlainObject`
+ source = source.replace(matchFunction(source, 'shimIsPlainObject'), function(match) {
+ return match.replace(/(?:\s*\/\/.*)*\n( *)if *\(iteratesOwnLast[\s\S]+?\n\1}/, '');
+ });
+
+ return source;
+ }
+
/**
* Removes the `Object.keys` object iteration optimization from `source`.
*
@@ -952,34 +1039,6 @@
return match.replace(/(?: *\/\/.*\n)* *["']( *)<% *if *\(isKeysFast[\s\S]+?["']\1<% *} *else *{ *%>.+\n([\s\S]+?) *["']\1<% *} *%>.+/, "'\\n' +\n$2");
});
- // remove data object property assignment in `createIterator`
- source = source.replace(matchFunction(source, 'createIterator'), function(match) {
- return match.replace(/ *'isKeysFast':.+\n/, '');
- });
-
- return source;
- }
-
- /**
- * Removes all `argsAreObjects` references from `source`.
- *
- * @private
- * @param {String} source The source to process.
- * @returns {String} Returns the modified source.
- */
- function removeArgsAreObjects(source) {
- source = removeVar(source, 'argsAreObjects');
-
- // remove `argsAreObjects` from `_.isArray`
- source = source.replace(matchFunction(source, 'isArray'), function(match) {
- return match.replace(/\(argsAreObjects && *([^)]+)\)/g, '$1');
- });
-
- // remove `argsAreObjects` from `_.isEqual`
- source = source.replace(matchFunction(source, 'isEqual'), function(match) {
- return match.replace(/!argsAreObjects[^:]+:\s*/g, '');
- });
-
return source;
}
@@ -1009,6 +1068,8 @@
* @returns {String} Returns the modified source.
*/
function removeNoCharByIndex(source) {
+ source = removeVar(source, 'noCharByIndex');
+
// remove `noCharByIndex` from `_.at`
source = source.replace(matchFunction(source, 'at'), function(match) {
return match.replace(/^ *if *\(noCharByIndex[^}]+}\n/m, '');
@@ -1024,6 +1085,36 @@
return match.replace(/noCharByIndex[^:]+:/, '');
});
+ // `noCharByIndex` from `iteratorTemplate`
+ source = source.replace(getIteratorTemplate(source), function(match) {
+ return match
+ .replace(/'if *\(<%= *arrays *%>[^']*/, '$&\\n')
+ .replace(/(?: *\/\/.*\n)* *["']( *)<% *if *\(noCharByIndex[\s\S]+?["']\1<% *} *%>.+/, '');
+ });
+
+ return source;
+ }
+
+ /**
+ * Removes all `nonEnumArgs` references from `source`.
+ *
+ * @private
+ * @param {String} source The source to process.
+ * @returns {String} Returns the modified source.
+ */
+ function removeNonEnumArgs(source) {
+ source = removeFromCreateIterator(source, 'nonEnumArgs');
+
+ // remove `nonEnumArgs` declaration and assignment
+ source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var nonEnumArgs;|.+?nonEnumArgs *=.+/g, '');
+
+ // remove `nonEnumArgs` fix from `iteratorTemplate`
+ source = source.replace(getIteratorTemplate(source), function(match) {
+ return match
+ .replace(/(?: *\/\/.*\n)*( *["'] *)<% *} *else *if *\(nonEnumArgs[\s\S]+?(\1<% *} *%>.+)/, '$2')
+ .replace(/ *\|\| *nonEnumArgs/, '');
+ });
+
return source;
}
@@ -1200,6 +1291,7 @@
'csp',
'legacy',
'mobile',
+ 'modern',
'modularize',
'strict',
'underscore',
@@ -1267,9 +1359,6 @@
// flag used to indicate that a custom IIFE was specified
var isIIFE = typeof iife == 'string';
- // flag used to specify an Underscore build
- var isUnderscore = isBackbone || options.indexOf('underscore') > -1;
-
// flag used to specify creating a source map for the minified source
var isMapped = options.indexOf('-p') > -1 || options.indexOf('--source-map') > -1;
@@ -1277,7 +1366,10 @@
var isMinify = options.indexOf('-m') > -1 || options.indexOf('--minify') > -1;
// flag used to specify a mobile build
- var isMobile = isCSP || isUnderscore || options.indexOf('mobile') > -1;
+ var isMobile = options.indexOf('mobile') > -1;
+
+ // flag used to specify a modern build
+ var isModern = isCSP || isMobile || options.indexOf('modern') > -1;
// flag used to specify a modularize build
var isModularize = options.indexOf('modularize') > -1;
@@ -1292,8 +1384,11 @@
// constructed using the "use strict" directive
var isStrict = options.indexOf('strict') > -1;
+ // flag used to specify an Underscore build
+ var isUnderscore = isBackbone || options.indexOf('underscore') > -1;
+
// flag used to specify a legacy build
- var isLegacy = !isMobile && options.indexOf('legacy') > -1;
+ var isLegacy = !(isModern || isUnderscore) && options.indexOf('legacy') > -1;
// used to specify methods of specific categories
var categories = options.reduce(function(result, value) {
@@ -1398,7 +1493,7 @@
useUnderscoreClone = methods.indexOf('clone') < 0;
}
// update dependencies
- if (isMobile) {
+ if (isModern || isUnderscore) {
dependencyMap.reduceRight = _.without(dependencyMap.reduceRight, 'isEqual', 'isString');
}
if (isUnderscore) {
@@ -1478,23 +1573,24 @@
source = replaceVar(source, 'noArgsClass', 'true');
source = removeKeysOptimization(source);
}
- if (isUnderscore) {
- // add Underscore style chaining
- source = addChainMethods(source);
+ if (isMobile || isUnderscore) {
+ source = removeKeysOptimization(source);
+ }
+ if (isModern || isUnderscore) {
+ source = removeHasDontEnumBug(source);
+ source = removeHasEnumPrototype(source);
+ source = removeIteratesOwnLast(source);
+ source = removeNoCharByIndex(source);
+ source = removeNonEnumArgs(source);
+ source = removeNoNodeClass(source);
+ }
+ if (isModern) {
+ // remove `_.isPlainObject` fallback
+ source = source.replace(matchFunction(source, 'isPlainObject'), function(match) {
+ return match.replace(/!getPrototypeOf.+?: */, '');
+ });
}
if (isUnderscore) {
- // remove unneeded variables
- if (useUnderscoreClone) {
- source = removeVar(source, 'cloneableClasses');
- source = removeVar(source, 'ctorByClass');
- }
- // remove `_.templateSettings.imports assignment
- source = source.replace(/,[^']*'imports':[^}]+}/, '');
-
- // remove large array optimizations
- source = removeFunction(source, 'cachedContains');
- source = removeVar(source, 'largeArraySize');
-
// replace `_.assign`
source = replaceFunction(source, 'assign', [
' function assign(object) {',
@@ -1901,6 +1997,16 @@
return match.replace(/^( *)lodash.find *=.+/m, '$&\n$1lodash.findWhere = findWhere;');
});
+ // add Underscore style chaining
+ source = addChainMethods(source);
+
+ // remove `_.templateSettings.imports assignment
+ source = source.replace(/,[^']*'imports':[^}]+}/, '');
+
+ // remove large array optimizations
+ source = removeFunction(source, 'cachedContains');
+ source = removeVar(source, 'largeArraySize');
+
// remove `_.isEqual` use from `createCallback`
source = source.replace(matchFunction(source, 'createCallback'), function(match) {
return match.replace(/isEqual\(([^,]+), *([^,]+)[^)]+\)/, '$1 === $2');
@@ -1913,6 +2019,11 @@
});
});
+ // remove unneeded variables
+ if (useUnderscoreClone) {
+ source = removeVar(source, 'cloneableClasses');
+ source = removeVar(source, 'ctorByClass');
+ }
// remove unused features from `createBound`
if (buildMethods.indexOf('partial') < 0 && buildMethods.indexOf('partialRight') < 0) {
source = source.replace(matchFunction(source, 'createBound'), function(match) {
@@ -1924,23 +2035,6 @@
});
}
}
- if (isMobile) {
- source = removeKeysOptimization(source);
- source = removeNoNodeClass(source);
-
- // remove `prototype` [[Enumerable]] fix from `_.keys`
- source = source.replace(matchFunction(source, 'keys'), function(match) {
- return match.replace(/(?:\s*\/\/.*)*(\s*return *).+?propertyIsEnumerable[\s\S]+?: */, '$1');
- });
-
- // remove `prototype` [[Enumerable]] fix from `iteratorTemplate`
- source = source.replace(getIteratorTemplate(source), function(match) {
- return match
- .replace(/(?: *\/\/.*\n)* *["']( *)<% *if *\(hasDontEnumBug[\s\S]+?["']\1<% *} *%>.+/, '')
- .replace(/(?: *\/\/.*\n)* *["'] *(?:<% *)?if *\(hasEnumPrototype *(?:&&|\))[\s\S]+?<% *} *(?:%>|["']).+/g, '')
- .replace(/hasEnumPrototype *\|\|/g, '');
- });
- }
vm.runInContext(source, context);
return context._;
}());
@@ -1951,15 +2045,6 @@
source = buildTemplate(templatePattern, templateSettings);
}
else {
- // simplify template snippets by removing unnecessary brackets
- source = source.replace(
- RegExp("{(\\\\n' *\\+\\s*.*?\\+\\n\\s*' *)}(?:\\\\n)?' *([,\\n])", 'g'), "$1'$2"
- );
-
- source = source.replace(
- RegExp("{(\\\\n' *\\+\\s*.*?\\+\\n\\s*' *)}(?:\\\\n)?' *\\+", 'g'), "$1;\\n'+"
- );
-
// remove methods from the build
allMethods.forEach(function(otherName) {
if (!_.contains(buildMethods, otherName)) {
@@ -2015,12 +2100,22 @@
source = removeIsArgumentsFallback(source);
}
- source = removeCreateFunction(source);
}
/*----------------------------------------------------------------------*/
- if (isMobile) {
+ if (isModern) {
+ source = removeArgsAreObjects(source);
+ source = removeHasObjectSpliceBug(source);
+ source = removeIsArgumentsFallback(source);
+ }
+ if (isModern || isUnderscore) {
+ source = removeNoArgsClass(source);
+ source = removeNoNodeClass(source);
+ }
+ if (isMobile || isUnderscore) {
+ source = removeVar(source, 'iteratorTemplate');
+
// inline all functions defined with `createIterator`
_.functions(lodash).forEach(function(methodName) {
// strip leading underscores to match pseudo private functions
@@ -2032,67 +2127,27 @@
});
}
});
+ }
+ if (isUnderscore) {
+ // remove `_.assign`, `_.forIn`, `_.forOwn`, and `_.isPlainObject` assignments
+ (function() {
+ var snippet = getMethodAssignments(source),
+ modified = snippet;
- if (isUnderscore) {
- // remove `_.assign`, `_.forIn`, `_.forOwn`, and `_.isPlainObject` assignments
- (function() {
- var snippet = getMethodAssignments(source),
- modified = snippet;
-
- if (!exposeAssign) {
- modified = modified.replace(/^(?: *\/\/.*\s*)* *lodash\.assign *= *.+\n/m, '');
- }
- if (!exposeForIn) {
- modified = modified.replace(/^(?: *\/\/.*\s*)* *lodash\.forIn *= *.+\n/m, '');
- }
- if (!exposeForOwn) {
- modified = modified.replace(/^(?: *\/\/.*\s*)* *lodash\.forOwn *= *.+\n/m, '');
- }
- if (!exposeIsPlainObject) {
- modified = modified.replace(/^(?: *\/\/.*\s*)* *lodash\.isPlainObject *= *.+\n/m, '');
- }
- source = source.replace(snippet, modified);
- }());
-
- // replace `noArgsClass` in the `_.isArguments` fallback
- source = source.replace(getIsArgumentsFallback(source), function(match) {
- return match.replace(/noArgsClass/g, '!isArguments(arguments)');
- });
-
- // remove chainability from `each` and `_.forEach`
- _.each(['each', 'forEach'], function(methodName) {
- source = source.replace(matchFunction(source, methodName), function(match) {
- return match.replace(/\n *return .+?([};\s]+)$/, '$1');
- });
- });
-
- // unexpose "exit early" feature of `each`, `_.forEach`, `_.forIn`, and `_.forOwn`
- _.each(['each', 'forEach', 'forIn', 'forOwn'], function(methodName) {
- source = source.replace(matchFunction(source, methodName), function(match) {
- return match.replace(/=== *false\)/g, '=== indicatorObject)');
- });
- });
-
- // modify `_.every`, `_.find`, `_.isEqual`, and `_.some` to use the private `indicatorObject`
- _.each(['every', 'isEqual'], function(methodName) {
- source = source.replace(matchFunction(source, methodName), function(match) {
- return match.replace(/\(result *= *(.+?)\);/g, '!(result = $1) && indicatorObject;');
- });
- });
-
- source = source.replace(matchFunction(source, 'find'), function(match) {
- return match.replace(/return false/, 'return indicatorObject');
- });
-
- source = source.replace(matchFunction(source, 'some'), function(match) {
- return match.replace(/!\(result *= *(.+?)\);/, '(result = $1) && indicatorObject;');
- });
- }
- else {
- source = removeArgsAreObjects(source);
- source = removeHasObjectSpliceBug(source);
- source = removeIsArgumentsFallback(source);
- }
+ if (!exposeAssign) {
+ modified = modified.replace(/^(?: *\/\/.*\s*)* *lodash\.assign *= *.+\n/m, '');
+ }
+ if (!exposeForIn) {
+ modified = modified.replace(/^(?: *\/\/.*\s*)* *lodash\.forIn *= *.+\n/m, '');
+ }
+ if (!exposeForOwn) {
+ modified = modified.replace(/^(?: *\/\/.*\s*)* *lodash\.forOwn *= *.+\n/m, '');
+ }
+ if (!exposeIsPlainObject) {
+ modified = modified.replace(/^(?: *\/\/.*\s*)* *lodash\.isPlainObject *= *.+\n/m, '');
+ }
+ source = source.replace(snippet, modified);
+ }());
// remove `thisArg` from unexposed `forIn` and `forOwn`
_.each([
@@ -2108,23 +2163,41 @@
}
});
- // remove `hasDontEnumBug`, `hasEnumPrototype`, `iteratesOwnLast`, and `nonEnumArgs` declarations and assignments
- source = source
- .replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var (?:hasDontEnumBug|hasEnumPrototype|iteratesOwnLast|nonEnumArgs).+\n/g, '')
- .replace(/^ *\(function\(\) *{[\s\S]+?}\(1\)\);\n/m, '');
-
- // remove `iteratesOwnLast` from `shimIsPlainObject`
- source = source.replace(matchFunction(source, 'shimIsPlainObject'), function(match) {
- return match.replace(/(?:\s*\/\/.*)*\n( *)if *\(iteratesOwnLast[\s\S]+?\n\1}/, '');
+ // replace `noArgsClass` in the `_.isArguments` fallback
+ source = source.replace(getIsArgumentsFallback(source), function(match) {
+ return match.replace(/noArgsClass/g, '!isArguments(arguments)');
});
- source = removeVar(source, 'iteratorTemplate');
- source = removeCreateFunction(source);
- source = removeNoArgsClass(source);
- source = removeNoCharByIndex(source);
- source = removeNoNodeClass(source);
+ // remove chainability from `each` and `_.forEach`
+ _.each(['each', 'forEach'], function(methodName) {
+ source = source.replace(matchFunction(source, methodName), function(match) {
+ return match.replace(/\n *return .+?([};\s]+)$/, '$1');
+ });
+ });
+
+ // unexpose "exit early" feature of `each`, `_.forEach`, `_.forIn`, and `_.forOwn`
+ _.each(['each', 'forEach', 'forIn', 'forOwn'], function(methodName) {
+ source = source.replace(matchFunction(source, methodName), function(match) {
+ return match.replace(/=== *false\)/g, '=== indicatorObject)');
+ });
+ });
+
+ // modify `_.every`, `_.find`, `_.isEqual`, and `_.some` to use the private `indicatorObject`
+ _.each(['every', 'isEqual'], function(methodName) {
+ source = source.replace(matchFunction(source, methodName), function(match) {
+ return match.replace(/\(result *= *(.+?)\);/g, '!(result = $1) && indicatorObject;');
+ });
+ });
+
+ source = source.replace(matchFunction(source, 'find'), function(match) {
+ return match.replace(/return false/, 'return indicatorObject');
+ });
+
+ source = source.replace(matchFunction(source, 'some'), function(match) {
+ return match.replace(/!\(result *= *(.+?)\);/, '(result = $1) && indicatorObject;');
+ });
}
- else {
+ if (!(isMobile || isUnderscore)) {
// inline `iteratorTemplate` template
source = source.replace(getIteratorTemplate(source), function() {
var snippet = getFunctionSource(lodash._iteratorTemplate);
@@ -2236,6 +2309,10 @@
.replace(/(?:\s*\/\/.*)*\s*lodash\.prototype.+\n/g, '')
.replace(/(?:\s*\/\/.*)*\s*mixin\(lodash\).+\n/, '');
}
+ if (!source.match(/var (?:hasDontEnumBug|hasEnumPrototype|iteratesOwnLast|nonEnumArgs)\b/g)) {
+ // remove `hasDontEnumBug`, `hasEnumPrototype`, `iteratesOwnLast`, and `nonEnumArgs` assignments
+ source = source.replace(/^ *\(function\(\) *{[\s\S]+?}\(1\)\);\n/m, '');
+ }
// assign debug source before further modifications that rely on the minifier
// to remove unused variables and other dead code
@@ -2268,9 +2345,9 @@
}
if ((source.match(/\bcreateIterator\b/g) || []).length < 2) {
source = removeFunction(source, 'createIterator');
- source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var hasDontEnumBug;|.+?hasDontEnumBug *=.+/g, '');
- source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var hasEnumPrototype;|.+?hasEnumPrototype *=.+/g, '');
- source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var nonEnumArgs;|.+?nonEnumArgs *=.+/g, '');
+ source = removeHasDontEnumBug(source);
+ source = removeHasEnumPrototype(source);
+ source = removeNonEnumArgs(source);
}
if (isRemoved(source, 'createIterator', 'bind', 'keys')) {
source = removeVar(source, 'isBindFast');
@@ -2281,10 +2358,6 @@
source = removeVar(source, 'nativeKeys');
source = removeKeysOptimization(source);
}
- if (!source.match(/var (?:hasDontEnumBug|hasEnumPrototype|iteratesOwnLast|nonEnumArgs)\b/g)) {
- // remove `hasDontEnumBug`, `hasEnumPrototype`, `iteratesOwnLast`, and `nonEnumArgs` assignments
- source = source.replace(/ *\(function\(\) *{[\s\S]+?}\(1\)\);\n/, '');
- }
}
source = cleanupSource(source);
@@ -2292,7 +2365,7 @@
/*------------------------------------------------------------------------*/
// used to specify creating a custom build
- var isCustom = isLegacy || isMapped || isMobile || isStrict ||
+ var isCustom = isLegacy || isMapped || isMobile || isModern || isStrict || isUnderscore ||
/(?:category|exclude|exports|iife|include|minus|plus)=/.test(options) ||
!_.isEqual(exportsOptions, exportsAll);
@@ -2315,14 +2388,17 @@
} else if (!isStdOut) {
callback({
'source': debugSource,
- 'outputPath': (isDebug && outputPath) || path.join(cwd, basename + '.js')
+ 'outputPath': outputPath || path.join(cwd, basename + '.js')
});
}
}
// begin the minification process
if (!isDebug) {
- outputPath || (outputPath = path.join(cwd, basename + '.min.js'));
-
+ if (outputPath && !isMinify) {
+ outputPath = path.join(path.dirname(outputPath), path.basename(outputPath, '.js') + '.min.js');
+ } else if (!outputPath) {
+ outputPath = path.join(cwd, basename + '.min.js');
+ }
minify(source, {
'filePath': filePath,
'isMapped': isMapped,
diff --git a/lodash.js b/lodash.js
index b64e842f4..c0b86b0df 100644
--- a/lodash.js
+++ b/lodash.js
@@ -362,7 +362,7 @@
// array-like iteration:
'<% if (arrays) { %>' +
'var length = iterable.length; index = -1;\n' +
- "if (<%= arrays %>) {" +
+ 'if (<%= arrays %>) {' +
// add support for accessing string characters by index if needed
' <% if (noCharByIndex) { %>\n' +
diff --git a/lodash.underscore.js b/lodash.underscore.js
index 838147d52..4cb66a1cd 100644
--- a/lodash.underscore.js
+++ b/lodash.underscore.js
@@ -67,12 +67,6 @@
/** Used to match unescaped characters in compiled string literals */
var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g;
- /** Used to fix the JScript [[DontEnum]] bug */
- var shadowed = [
- 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
- 'toLocaleString', 'toString', 'valueOf'
- ];
-
/** Used to make template sourceURLs easier to identify */
var templateCounter = 0;
@@ -127,17 +121,11 @@
var hasObjectSpliceBug = (hasObjectSpliceBug = { '0': 1, 'length': 1 },
arrayRef.splice.call(hasObjectSpliceBug, 0, 1), hasObjectSpliceBug[0]);
+ /** Detect if an `arguments` object's indexes are non-enumerable (IE < 9) */
+
/** Detect if `arguments` objects are `Object` objects (all but Opera < 10.5) */
var argsAreObjects = arguments.constructor == Object;
- /**
- * Detect lack of support for accessing string characters by index:
- *
- * IE < 8 can't access characters by index and IE 8 can only access
- * characters by index on string literals.
- */
- var noCharByIndex = ('x'[0] + Object('x')[0]) != 'xx';
-
/** Used to determine if values are of the language type Object */
var objectTypes = {
'boolean': false,
@@ -454,11 +442,6 @@
function createIterator() {
var data = {
// support properties
- 'hasDontEnumBug': hasDontEnumBug,
- 'hasEnumPrototype': hasEnumPrototype,
- 'nonEnumArgs': nonEnumArgs,
- 'noCharByIndex': noCharByIndex,
- 'shadowed': shadowed,
// iterator options
'arrays': 'isArray(iterable)',
@@ -508,7 +491,7 @@
if (!iterable) return result;
callback = callback && typeof thisArg == 'undefined' ? callback : createCallback(callback, thisArg);
var length = iterable.length; index = -1;
- if (typeof length == 'number') {
+ if (typeof length == 'number') {
while (++index < length) {
if (callback(iterable[index], index, collection) === indicatorObject) return result
}
diff --git a/test/test-build.js b/test/test-build.js
index d9ac7f0e4..e18887d26 100644
--- a/test/test-build.js
+++ b/test/test-build.js
@@ -73,7 +73,8 @@
/** List of all Lo-Dash methods */
var allMethods = _.functions(_)
.filter(function(methodName) { return !/^_/.test(methodName); })
- .concat('chain');
+ .concat('chain')
+ .sort();
/** List of "Arrays" category methods */
var arraysMethods = [
@@ -1102,6 +1103,7 @@
'csp',
'legacy',
'mobile',
+ 'modern',
'strict',
'underscore',
'category=arrays',
@@ -1114,7 +1116,6 @@
'include=each,filter,map',
'include=once plus=bind,Chaining',
'category=collections,functions',
- 'underscore backbone',
'backbone legacy category=utilities minus=first,last',
'underscore include=debounce,throttle plus=after minus=throttle',
'underscore mobile strict category=functions exports=amd,global plus=pick,uniq',
@@ -1126,7 +1127,7 @@
);
commands.forEach(function(origCommand) {
- _.times(3, function(index) {
+ _.times(4, function(index) {
var command = origCommand;
if (index == 1) {
@@ -1136,6 +1137,12 @@
command = 'mobile ' + command;
}
if (index == 2) {
+ if (/modern/.test(command)) {
+ return;
+ }
+ command = 'modern ' + command;
+ }
+ if (index == 3) {
if (/category|underscore/.test(command)) {
return;
}