#!/usr/bin/env node ;(function() { 'use strict'; /** Load Node.js modules */ var fs = require('fs'), path = require('path'), vm = require('vm'); /** Load other modules */ var _ = require(path.join(__dirname, 'lodash.js')), minify = require(path.join(__dirname, 'build', 'minify.js')), mkdirpSync = require(path.join(__dirname, 'build', 'mkdirp-sync.js')); /** Add `path.sep` for older versions of Node.js */ path.sep || (path.sep = process.platform == 'win32' ? '\\' : '/'); /** The current working directory */ var cwd = process.cwd(); /** Used for array method references */ var arrayRef = []; /** Shortcut used to push arrays of values to an array */ var push = arrayRef.push; /** Used to detect the Node.js executable in command-line arguments */ var reNode = RegExp('(?:^|' + path.sep + ')node(?:\\.exe)?$'); /** Shortcut used to convert array-like objects to arrays */ var slice = arrayRef.slice; /** Shortcut to the `stdout` object */ var stdout = process.stdout; /** Used to associate aliases with their real names */ var aliasToRealMap = { 'all': 'every', 'any': 'some', 'collect': 'map', 'detect': 'find', 'drop': 'rest', 'each': 'forEach', 'extend': 'assign', 'foldl': 'reduce', 'foldr': 'reduceRight', 'head': 'first', 'include': 'contains', 'inject': 'reduce', 'methods': 'functions', 'object': 'zipObject', 'select': 'filter', 'tail': 'rest', 'take': 'first', 'unique': 'uniq' }; /** Used to associate real names with their aliases */ var realToAliasMap = { 'assign': ['extend'], 'contains': ['include'], 'every': ['all'], 'filter': ['select'], 'find': ['detect'], 'first': ['head', 'take'], 'forEach': ['each'], 'functions': ['methods'], 'map': ['collect'], 'reduce': ['foldl', 'inject'], 'reduceRight': ['foldr'], 'rest': ['drop', 'tail'], 'some': ['any'], 'uniq': ['unique'], 'zipObject': ['object'] }; /** Used to track function dependencies */ var dependencyMap = { 'after': [], 'assign': ['isArray', 'forEach', 'forOwn'], 'at': ['isString'], 'bind': ['isFunction', 'isObject'], 'bindAll': ['bind', 'functions'], 'bindKey': ['isFunction', 'isObject'], 'clone': ['assign', 'forEach', 'forOwn', 'isArray', 'isObject'], 'cloneDeep': ['clone'], 'compact': [], 'compose': [], 'contains': ['indexOf', 'isString'], 'countBy': ['createCallback', 'forEach'], 'createCallback': ['identity', 'isEqual', 'keys'], 'debounce': [], 'defaults': ['isArray', 'forEach', 'forOwn'], 'defer': ['bind'], 'delay': [], 'difference': ['indexOf'], 'escape': [], 'every': ['createCallback', 'isArray'], 'filter': ['createCallback', 'isArray'], 'find': ['createCallback', 'forEach'], 'first': [], 'flatten': ['createCallback', 'isArray'], 'forEach': ['createCallback', 'isArguments', 'isArray', 'isString'], 'forIn': ['createCallback', 'isArguments'], 'forOwn': ['createCallback', 'isArguments'], 'functions': ['forIn', 'isFunction'], 'groupBy': ['createCallback', 'forEach'], 'has': [], 'identity': [], 'indexOf': ['sortedIndex'], 'initial': [], 'intersection': ['indexOf'], 'invert': ['keys'], 'invoke': ['forEach'], 'isArguments': [], 'isArray': [], 'isBoolean': [], 'isDate': [], 'isElement': [], 'isEmpty': ['forOwn', 'isArguments', 'isFunction'], 'isEqual': ['forIn', 'isArguments', 'isFunction'], 'isFinite': [], 'isFunction': [], 'isNaN': ['isNumber'], 'isNull': [], 'isNumber': [], 'isObject': [], 'isPlainObject': ['forIn', 'isArguments', 'isFunction'], 'isRegExp': [], 'isString': [], 'isUndefined': [], 'keys': ['forOwn', 'isArguments', 'isObject'], 'last': [], 'lastIndexOf': [], 'map': ['createCallback', 'isArray'], 'max': ['createCallback', 'isArray', 'isString'], 'memoize': [], 'merge': ['forEach', 'forOwn', 'isArray', 'isObject', 'isPlainObject'], 'min': ['createCallback', 'isArray', 'isString'], 'mixin': ['forEach', 'functions'], 'noConflict': [], 'omit': ['forIn', 'indexOf'], 'once': [], 'pairs': ['keys'], 'parseInt': ['isString'], 'partial': ['isFunction', 'isObject'], 'partialRight': ['isFunction', 'isObject'], 'pick': ['forIn', 'isObject'], 'pluck': ['map'], 'random': [], 'range': [], 'reduce': ['createCallback', 'isArray'], 'reduceRight': ['createCallback', 'forEach', 'isString', 'keys'], 'reject': ['createCallback', 'filter'], 'rest': [], 'result': ['isFunction'], 'runInContext': ['defaults', 'pick'], 'shuffle': ['forEach'], 'size': ['keys'], 'some': ['createCallback', 'isArray'], 'sortBy': ['createCallback', 'forEach'], 'sortedIndex': ['createCallback', 'identity'], 'tap': ['value'], 'template': ['defaults', 'escape', 'keys', 'values'], 'throttle': [], 'times': [], 'toArray': ['isString', 'values'], 'unescape': [], 'union': ['uniq'], 'uniq': ['createCallback', 'indexOf'], 'uniqueId': [], 'value': ['forOwn'], 'values': ['keys'], 'where': ['filter'], 'without': ['indexOf'], 'wrap': [], 'zip': ['max', 'pluck'], 'zipObject': [], // method used by the `backbone` and `underscore` builds 'chain': ['value'], 'findWhere': ['where'] }; /** Used to inline `iteratorTemplate` */ var iteratorOptions = [ 'args', 'arrays', 'bottom', 'firstArg', 'loop', 'shadowedProps', 'top', 'useHas' ]; /** List of all Lo-Dash methods */ var allMethods = _.keys(dependencyMap); /** List of Backbone's Lo-Dash dependencies */ var backboneDependencies = [ 'bind', 'bindAll', 'chain', 'clone', 'contains', 'countBy', 'defaults', 'escape', 'every', 'extend', 'filter', 'find', 'first', 'forEach', 'groupBy', 'has', 'indexOf', 'initial', 'invoke', 'isArray', 'isEmpty', 'isEqual', 'isFunction', 'isObject', 'isRegExp', 'isString', 'keys', 'last', 'lastIndexOf', 'map', 'max', 'min', 'mixin', 'once', 'pick', 'reduce', 'reduceRight', 'reject', 'rest', 'result', 'shuffle', 'size', 'some', 'sortBy', 'sortedIndex', 'toArray', 'uniqueId', 'value', 'without' ]; /** List of methods used by Underscore */ var underscoreMethods = _.without.apply(_, [allMethods].concat([ 'at', 'bindKey', 'cloneDeep', 'createCallback', 'forIn', 'forOwn', 'isPlainObject', 'merge', 'parseInt', 'partialRight', 'runInContext' ])); /** List of ways to export the `lodash` function */ var exportsAll = [ 'amd', 'commonjs', 'global', 'node' ]; /** Add `path.sep` for older versions of Node.js */ path.sep || (path.sep = process.platform == 'win32' ? '\\' : '/'); /*--------------------------------------------------------------------------*/ /** * Adds support for Underscore style chaining to the `source`. * * @private * @param {String} source The source to process. * @returns {String} Returns the modified source. */ function addChainMethods(source) { // add `_.chain` source = source.replace(matchFunction(source, 'tap'), function(match) { var indent = getIndent(match); return match && (indent + [ '', '/**', ' * Creates a `lodash` object that wraps the given `value`.', ' *', ' * @static', ' * @memberOf _', ' * @category Chaining', ' * @param {Mixed} value The value to wrap.', ' * @returns {Object} Returns the wrapper object.', ' * @example', ' *', ' * var stooges = [', " * { 'name': 'moe', 'age': 40 },", " * { 'name': 'larry', 'age': 50 },", " * { 'name': 'curly', 'age': 60 }", ' * ];', ' *', ' * var youngest = _.chain(stooges)', ' * .sortBy(function(stooge) { return stooge.age; })', " * .map(function(stooge) { return stooge.name + ' is ' + stooge.age; })", ' * .first();', " * // => 'moe is 40'", ' */', 'function chain(value) {', ' value = new lodashWrapper(value);', ' value.__chain__ = true;', ' return value;', '}', '', match ].join('\n' + indent)); }); // add `wrapperChain` source = source.replace(matchFunction(source, 'wrapperToString'), function(match) { var indent = getIndent(match); return match && (indent + [ '', '/**', ' * Enables method chaining on the wrapper object.', ' *', ' * @name chain', ' * @memberOf _', ' * @category Chaining', ' * @returns {Mixed} Returns the wrapper object.', ' * @example', ' *', ' * var sum = _([1, 2, 3])', ' * .chain()', ' * .reduce(function(sum, num) { return sum + num; })', ' * .value()', ' * // => 6`', ' */', 'function wrapperChain() {', ' this.__chain__ = true;', ' return this;', '}', '', match ].join('\n' + indent)); }); // add `lodash.chain` assignment source = source.replace(getMethodAssignments(source), function(match) { return match.replace(/^(?: *\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/\n)?( *)lodash\.VERSION *=/m, '$1lodash.chain = chain;\n\n$&'); }); // add `lodash.prototype.chain` assignment source = source.replace(/^( *)lodash\.prototype\.value *=.+\n/m, '$1lodash.prototype.chain = wrapperChain;\n$&'); // remove `lodash.prototype.toString` and `lodash.prototype.valueOf` assignments source = source.replace(/^ *lodash\.prototype\.(?:toString|valueOf) *=.+\n/gm, ''); // remove `lodash.prototype` batch method assignments source = source.replace(/(?:\s*\/\/.*)*\n( *)forOwn\(lodash, *function\(func, *methodName\)[\s\S]+?\n\1}.+/g, ''); // move `mixin(lodash)` to after the method assignments source = source.replace(/(?:\s*\/\/.*)*\n( *)mixin\(lodash\).+/, ''); source = source.replace(getMethodAssignments(source), function(match) { var indent = /^ *(?=lodash\.)/m.exec(match)[0]; return match + [ '', '', '// add functions to `lodash.prototype`', 'mixin(lodash);' ].join('\n' + indent); }); // replace `_.mixin` source = replaceFunction(source, 'mixin', [ 'function mixin(object) {', ' forEach(functions(object), function(methodName) {', ' var func = lodash[methodName] = object[methodName];', '', ' lodash.prototype[methodName] = function() {', ' var args = [this.__wrapped__];', ' push.apply(args, arguments);', '', ' var result = func.apply(lodash, args);', ' if (this.__chain__) {', ' result = new lodashWrapper(result);', ' result.__chain__ = true;', ' }', ' return result;', ' };', ' });', '}' ].join('\n')); // replace wrapper `Array` method assignments source = source.replace(/^(?: *\/\/.*\n)*( *)each\(\['[\s\S]+?\n\1}$/m, function(match, indent) { return indent + [ '// add `Array` mutator functions to the wrapper', "each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) {", ' var func = arrayRef[methodName];', ' lodash.prototype[methodName] = function() {', ' var value = this.__wrapped__;', ' func.apply(value, arguments);', '', ' // avoid array-like object bugs with `Array#shift` and `Array#splice`', ' // in Firefox < 10 and IE < 9', ' if (!support.spliceObjects && value.length === 0) {', ' delete value[0];', ' }', ' return this;', ' };', '});', '', '// add `Array` accessor functions to the wrapper', "each(['concat', 'join', 'slice'], function(methodName) {", ' var func = arrayRef[methodName];', ' lodash.prototype[methodName] = function() {', ' var value = this.__wrapped__,', ' result = func.apply(value, arguments);', '', ' if (this.__chain__) {', ' result = new lodashWrapper(result);', ' result.__chain__ = true;', ' }', ' return result;', ' };', '});' ].join('\n' + indent); }); return source; } /** * Adds build `commands` to the copyright/license header of the `source`. * * @private * @param {String} source The source to process. * @param {Array} [commands=[]] An array of commands. * @returns {String} Returns the modified source. */ function addCommandsToHeader(source, commands) { return source.replace(/(\/\**\n)( \*)( *@license[\s*]+)( *Lo-Dash [\w.-]+)(.*)/, function() { // remove `node path/to/build.js` from `commands` if (reNode.test(commands[0])) { commands.splice(0, 2); } // add quotes to commands with spaces or equals signs commands = _.map(commands, function(command) { var separator = (command.match(/[= ]/) || [''])[0]; if (separator) { var pair = command.split(separator); command = pair[0] + separator + '"' + pair[1] + '"'; } // escape newlines, carriage returns, multi-line comment end tokens command = command .replace(/\n/g, '\\n') .replace(/\r/g, '\\r') .replace(/\*\//g, '*\\/'); return command; }); // add build commands to copyright/license header var parts = slice.call(arguments, 1); return ( parts[0] + parts[1] + parts[2] + parts[3] + ' (Custom Build)' + parts[4] + '\n' + parts[1] + ' Build: `lodash ' + commands.join(' ') + '`' ); }); } /** * Compiles template files matched by the given file path `pattern` into a * single source, extending `_.templates` with precompiled templates named after * each template file's basename. * * @private * @param {String} [pattern='/*.jst'] The file path pattern. * @param {Object} options The options object. * @returns {String} Returns the compiled source. */ function buildTemplate(pattern, options) { pattern || (pattern = path.join(cwd, '*.jst')); var directory = path.dirname(pattern); var source = [ ';(function(window) {', " var freeExports = typeof exports == 'object' && typeof require == 'function' && exports;", '', " var freeModule = typeof module == 'object' && module && module.exports == freeExports && module;", '', " var freeGlobal = typeof global == 'object' && global;", ' if (freeGlobal.global === freeGlobal) {', ' window = freeGlobal;', ' }', '', ' var templates = {},', ' _ = window._;', '' ]; // convert to a regexp pattern = RegExp( path.basename(pattern) .replace(/[.+?^=!:${}()|[\]\/\\]/g, '\\$&') .replace(/\*/g, '.*?') + '$' ); fs.readdirSync(directory).forEach(function(filename) { var filePath = path.join(directory, filename); if (pattern.test(filename)) { var text = fs.readFileSync(filePath, 'utf8'), precompiled = getFunctionSource(_.template(text, null, options)), prop = filename.replace(/\..*$/, ''); source.push(" templates['" + prop.replace(/['\n\r\t]/g, '\\$&') + "'] = " + precompiled + ';', ''); } }); source.push( " if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {", " define(['" + options.moduleId + "'], function(lodash) {", ' _ = lodash;', ' lodash.templates = lodash.extend(lodash.templates || {}, templates);', ' });', " } else if (freeExports && !freeExports.nodeType) {", " _ = require('" + options.moduleId + "');", " if (freeModule) {", ' (freeModule.exports = templates).templates = templates;', ' } else {', ' freeExports.templates = templates;', ' }', ' } else if (_) {', ' _.templates = _.extend(_.templates || {}, templates);', ' }', '}(this));' ); return source.join('\n'); } /** * Capitalizes a given string. * * @private * @param {String} string The string to capitalize. * @returns {String} Returns the capitalized string. */ function capitalize(string) { return string[0].toUpperCase() + string.toLowerCase().slice(1); } /** * Removes unnecessary comments, whitespace, and pseudo private properties. * * @private * @param {String} source The source to process. * @returns {String} Returns the modified source. */ function cleanupSource(source) { return source // remove pseudo private properties .replace(/(?:(?:\s*\/\/.*)*\s*lodash\._[^=]+=.+\n)+/g, '\n') // remove extraneous whitespace .replace(/^ *\n/gm, '\n') // remove lines with just whitespace and semicolons .replace(/^ *;\n/gm, '') // consolidate multiple newlines .replace(/\n{3,}/g, '\n\n') // consolidate consecutive horizontal rule comment separators .replace(/(?:\s*\/\*-+\*\/\s*){2,}/g, function(separators) { return separators.match(/^\s*/)[0] + separators.slice(separators.lastIndexOf('/*')); }); } /** * Writes the help message to standard output. * * @private */ function displayHelp() { console.log([ '', ' Commands:', '', ' lodash backbone Build with only methods required by Backbone', ' lodash csp Build supporting default Content Security Policy restrictions', ' 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 most 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', ' lodash minus=... Comma separated method/category names to remove from those included in the build', ' lodash plus=... Comma separated method/category names to add to those included in the build', ' lodash category=... Comma separated categories of methods to include in the build (case-insensitive)', ' (i.e. “arrays”, “chaining”, “collections”, “functions”, “objects”, and “utilities”)', ' lodash exports=... Comma separated names of ways to export the `lodash` function', ' (i.e. “amd”, “commonjs”, “global”, “node”, and “none”)', ' lodash iife=... Code to replace the immediately-invoked function expression that wraps Lo-Dash', ' (e.g. `lodash iife="!function(window){%output%}(this)"`)', '', ' lodash template=... File path pattern used to match template files to precompile', ' (e.g. `lodash template=./*.jst`)', ' lodash settings=... Template settings used when precompiling templates', ' (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`, `mobile`, `modern`, or `underscore`, may be combined.', ' Unless specified by `-o` or `--output`, all files created are saved to the current working directory.', '', ' Options:', '', ' -c, --stdout Write output to standard output', ' -d, --debug Write only the non-minified development output', ' -h, --help Display help information', ' -m, --minify Write only the minified production output', ' -o, --output Write output to a given path/filename', ' -p, --source-map Generate a source map for the minified output, using an optional source map URL', ' -s, --silent Skip status updates normally logged to the console', ' -V, --version Output current version of Lo-Dash', '' ].join('\n')); } /** * Gets the aliases associated with a given function name. * * @private * @param {String} methodName The name of the method to get aliases for. * @returns {Array} Returns an array of aliases. */ function getAliases(methodName) { return realToAliasMap[methodName] || []; } /** * Gets the category of the given method name. * * @private * @param {String} source The source to inspect. * @param {String} methodName The method name. * @returns {String} Returns the method name's category. */ function getCategory(source, methodName) { var result = /@category +(\w+)/.exec(matchFunction(source, methodName)); return result ? result[1] : ''; } /** * Gets an array of category dependencies for a given category. * * @private * @param {String} source The source to inspect. * @param {String} category The category. * @returns {Array} Returns an array of cetegory dependants. */ function getCategoryDependencies(source, category) { var methods = _.uniq(getMethodsByCategory(source, category).reduce(function(result, methodName) { push.apply(result, getDependencies(methodName)); return result; }, [])); var categories = _.uniq(methods.map(function(methodName) { return getCategory(source, methodName); })); return categories.filter(function(other) { return other != category; }); } /** * Gets an array of depenants for a method by a given name. * * @private * @param {String} methodName The method name. * @returns {Array} Returns an array of method dependants. */ function getDependants(methodName) { // iterate over the `dependencyMap`, adding the names of methods that // have `methodName` as a dependency return _.reduce(dependencyMap, function(result, dependencies, otherName) { if (_.contains(dependencies, methodName)) { result.push(otherName); } return result; }, []); } /** * Gets an array of dependencies for a given method name. If passed an array * of dependencies it will return an array containing the given dependencies * plus any additional detected sub-dependencies. * * @private * @param {Array|String} methodName A single method name or array of * dependencies to query. * @param- {Object} [stackA=[]] Internally used track queried methods. * @returns {Array} Returns an array of method dependencies. */ function getDependencies(methodName, stack) { var dependencies = Array.isArray(methodName) ? methodName : dependencyMap[methodName]; if (!dependencies) { return []; } stack || (stack = []); // recursively accumulate the dependencies of the `methodName` function, and // the dependencies of its dependencies, and so on return _.uniq(dependencies.reduce(function(result, otherName) { if (stack.indexOf(otherName) < 0) { stack.push(otherName); result.push.apply(result, getDependencies(otherName, stack).concat(otherName)); } return result; }, [])); } /** * Gets the formatted source of the given function. * * @private * @param {Function} func The function to process. * @param {String} indent The function indent. * @returns {String} Returns the formatted source. */ function getFunctionSource(func, indent) { var source = func.source || (func + ''); if (indent == null) { indent = ' '; } // format leading whitespace return source.replace(/\n(?:.*)/g, function(match, index) { match = match.slice(1); return ( '\n' + indent + (match == '}' && source.indexOf('}', index + 2) < 0 ? '' : ' ') ) + match; }); } /** * Gets the indent of the given function. * * @private * @param {Function} func The function to process. * @returns {String} Returns the indent. */ function getIndent(func) { return /^ *(?=\S)/m.exec(func.source || func)[0]; } /** * Gets the `_.isArguments` fallback from `source`. * * @private * @param {String} source The source to inspect. * @returns {String} Returns the `isArguments` fallback. */ function getIsArgumentsFallback(source) { return (source.match(/(?:\s*\/\/.*)*\n( *)if *\((?:!support\.argsClass|!isArguments)[\s\S]+?};\n\1}/) || [''])[0]; } /** * Gets the `_.isFunction` fallback from `source`. * * @private * @param {String} source The source to inspect. * @returns {String} Returns the `isFunction` fallback. */ function getIsFunctionFallback(source) { return (source.match(/(?:\s*\/\/.*)*\n( *)if *\(isFunction\(\/x\/[\s\S]+?};\n\1}/) || [''])[0]; } /** * Gets the `iteratorTemplate` from `source`. * * @private * @param {String} source The source to inspect. * @returns {String} Returns the `iteratorTemplate`. */ function getIteratorTemplate(source) { return (source.match(/^( *)var iteratorTemplate *= *[\s\S]+?\n\1.+?;\n/m) || [''])[0]; } /** * Gets the Lo-Dash method assignments snippet from `source`. * * @private * @param {String} source The source to inspect. * @returns {String} Returns the method assignments snippet. */ function getMethodAssignments(source) { return (source.match(/\/\*-+\*\/\n(?:\s*\/\/.*)*\s*lodash\.\w+ *=[\s\S]+?lodash\.VERSION *=.+/) || [''])[0]; } /** * Gets the names of methods in `source` belonging to the given `category`. * * @private * @param {String} source The source to inspect. * @param {String} category The category to filter by. * @returns {Array} Returns a new array of method names belonging to the given category. */ function getMethodsByCategory(source, category) { return allMethods.filter(function(methodName) { return getCategory(source, methodName) == category; }); } /** * Gets the real name, not alias, of a given method name. * * @private * @param {String} methodName The name of the method to resolve. * @returns {String} Returns the real method name. */ function getRealName(methodName) { return aliasToRealMap[methodName] || methodName; } /** * Determines if all functions of the given names have been removed from `source`. * * @private * @param {String} source The source to inspect. * @param {String} [funcName1, funcName2, ...] The names of functions to check. * @returns {Boolean} Returns `true` if all functions have been removed, else `false`. */ function isRemoved(source) { return slice.call(arguments, 1).every(function(funcName) { return !( matchFunction(source, funcName) || RegExp('^ *lodash\\.prototype\\.' + funcName + ' *=.+', 'm').test(source) ); }); } /** * Searches `source` for a `funcName` function declaration, expression, or * assignment and returns the matched snippet. * * @private * @param {String} source The source to inspect. * @param {String} funcName The name of the function to match. * @returns {String} Returns the matched function snippet. */ function matchFunction(source, funcName) { var result = source.match(RegExp( // match multi-line comment block '(?:\\n +/\\*[^*]*\\*+(?:[^/][^*]*\\*+)*/)?\\n' + // begin non-capturing group '( *)(?:' + // match a function declaration 'function ' + funcName + '\\b[\\s\\S]+?\\n\\1}|' + // match a variable declaration with function expression 'var ' + funcName + ' *=.*?function[\\s\\S]+?\\n\\1};' + // end non-capturing group ')\\n' )); // match variables that are explicitly defined as functions result || (result = source.match(RegExp( // match multi-line comment block '(?:\\n +/\\*[^*]*\\*+(?:[^/][^*]*\\*+)*/)?\\n' + // match simple variable declarations and those with `createIterator` ' *var ' + funcName + ' *=(?:.+?|.*?createIterator\\([\\s\\S]+?\\));\\n' ))); return /@type +Function|function\s*\w*\(/.test(result) ? result[0] : ''; } /** * Converts a comma separated options string into an array. * * @private * @param {String} value The option to convert. * @returns {Array} Returns the new converted array. */ function optionToArray(value) { return _.compact(value.match(/\w+=(.*)$/)[1].split(/, */)); } /** * Converts a comma separated options string into an array containing * only real method names. * * @private * @param {String} source The source to inspect. * @param {String} value The option to convert. * @returns {Array} Returns the new converted array. */ function optionToMethodsArray(source, value) { var methodNames = optionToArray(value); // convert categories to method names methodNames.forEach(function(category) { push.apply(methodNames, getMethodsByCategory(source, category)); }); // convert aliases to real method names methodNames = methodNames.map(getRealName); // remove nonexistent and duplicate method names return _.uniq(_.intersection(allMethods, methodNames)); } /** * Removes the all references to `varName` from `createIterator` in `source`. * * @private * @param {String} source The source to process. * @param {String} varName The name of the variable to remove. * @returns {String} Returns the modified source. */ function removeFromCreateIterator(source, varName) { var snippet = matchFunction(source, 'createIterator'); if (!snippet) { return source; } // remove data object property assignment var modified = snippet.replace(RegExp("^(?: *\\/\\/.*\\n)* *'" + varName + "': *" + varName + '.+\\n+', 'm'), ''); source = source.replace(snippet, function() { return modified; }); // clip at the `factory` assignment snippet = modified.match(/Function\([\s\S]+$/)[0]; modified = snippet .replace(RegExp('\\b' + varName + '\\b,? *', 'g'), '') .replace(/, *',/, "',") .replace(/,\s*\)/, ')') source = source.replace(snippet, function() { return modified; }); return source; } /** * Removes the `funcName` function declaration, expression, or assignment and * associated code from `source`. * * @private * @param {String} source The source to process. * @param {String} funcName The name of the function to remove. * @returns {String} Returns the modified source. */ function removeFunction(source, funcName) { var snippet; // remove function if (funcName == 'runInContext') { source = removeRunInContext(source, funcName); } else if ((snippet = matchFunction(source, funcName))) { source = source.replace(snippet, ''); } // grab the method assignments snippet snippet = getMethodAssignments(source); // remove method assignment from `lodash.prototype` source = source.replace(RegExp('^ *lodash\\.prototype\\.' + funcName + ' *=.+\\n', 'm'), ''); // remove assignment and aliases var modified = getAliases(funcName).concat(funcName).reduce(function(result, otherName) { return result.replace(RegExp('^(?: *//.*\\s*)* *lodash\\.' + otherName + ' *=.+\\n', 'm'), ''); }, snippet); // replace with the modified snippet source = source.replace(snippet, function() { return modified; }); return removeFromCreateIterator(source, funcName); } /** * Removes the `_.isArguments` fallback from `source`. * * @private * @param {String} source The source to process. * @returns {String} Returns the modified source. */ function removeIsArgumentsFallback(source) { return source.replace(getIsArgumentsFallback(source), ''); } /** * Removes the `_.isFunction` fallback from `source`. * * @private * @param {String} source The source to process. * @returns {String} Returns the modified source. */ function removeIsFunctionFallback(source) { return source.replace(getIsFunctionFallback(source), ''); } /** * Removes the `Object.keys` object iteration optimization from `source`. * * @private * @param {String} source The source to process. * @returns {String} Returns the modified source. */ function removeKeysOptimization(source) { source = removeVar(source, 'isJSC'); source = removeSupportProp(source, 'fastKeys'); // remove optimized branch in `iteratorTemplate` source = source.replace(getIteratorTemplate(source), function(match) { return match.replace(/^(?: *\/\/.*\n)* *["']( *)<% *if *\(support\.fastKeys[\s\S]+?["']\1<% *} *else *{ *%>.+\n([\s\S]+?) *["']\1<% *} *%>.+/m, "'\\n' +\n$2"); }); return source; } /** * Removes all `lodashWrapper` references from `source`. * * @private * @param {String} source The source to process. * @returns {String} Returns the modified source. */ function removeLodashWrapper(source) { source = removeFunction(source, 'lodashWrapper'); // remove `lodashWrapper.prototype` assignment source = source.replace(/(?:\s*\/\/.*)*\n *lodashWrapper\.prototype *=.+/, ''); // replace `new lodashWrapper` with `new lodash` source = source.replace(/\bnew lodashWrapper\b/g, 'new lodash'); return source; } /** * Removes all `support.argsObject` references from `source`. * * @private * @param {String} source The source to process. * @returns {String} Returns the modified source. */ function removeSupportArgsObject(source) { source = removeSupportProp(source, 'argsObject'); // remove `argsAreObjects` from `_.isArray` source = source.replace(matchFunction(source, 'isArray'), function(match) { return match.replace(/\(support\.argsObject && *([^)]+)\)/g, '$1'); }); // remove `argsAreObjects` from `_.isEqual` source = source.replace(matchFunction(source, 'isEqual'), function(match) { return match.replace(/!support.\argsObject[^:]+:\s*/g, ''); }); return source; } /** * Removes all `support.argsClass` references from `source`. * * @private * @param {String} source The source to process. * @returns {String} Returns the modified source. */ function removeSupportArgsClass(source) { source = removeSupportProp(source, 'argsClass'); // replace `support.argsClass` in the `_.isArguments` fallback source = source.replace(getIsArgumentsFallback(source), function(match) { return match.replace(/!support\.argsClass/g, '!isArguments(arguments)'); }); // remove `support.argsClass` from `_.isEmpty` source = source.replace(matchFunction(source, 'isEmpty'), function(match) { return match.replace(/\s*\(support\.argsClass *\?([^:]+):.+?\)\)/g, '$1'); }); return source; } /** * Removes all `support.enumPrototypes` references from `source`. * * @private * @param {String} source The source to process. * @returns {String} Returns the modified source. */ function removeSupportEnumPrototypes(source) { source = removeSupportProp(source, 'enumPrototypes'); // remove `support.enumPrototypes` from `_.keys` source = source.replace(matchFunction(source, 'keys'), function(match) { return match .replace(/\(support\.enumPrototypes[^)]+\)(?:\s*\|\|\s*)?/, '') .replace(/\s*if *\(\s*\)[^}]+}/, ''); }); // remove `support.enumPrototypes` from `iteratorTemplate` source = source.replace(getIteratorTemplate(source), function(match) { return match .replace(/(?: *\/\/.*\n)* *["'] *(?:<% *)?if *\(support\.enumPrototypes *(?:&&|\))[\s\S]+?<% *} *(?:%>|["']).+/g, '') .replace(/support\.enumPrototypes *\|\|\s*/g, ''); }); return source; } /** * Removes all `support.nodeClass` references from `source`. * * @private * @param {String} source The source to process. * @returns {String} Returns the modified source. */ function removeSupportNodeClass(source) { source = removeSupportProp(source, 'nodeClass'); // remove `support.nodeClass` from `shimIsPlainObject` source = source.replace(matchFunction(source, 'shimIsPlainObject'), function(match) { return match.replace(/ *&& *\(support\.nodeClass[\s\S]+?\)\)/, ''); }); // remove `support.nodeClass` from `_.clone` source = source.replace(matchFunction(source, 'clone'), function(match) { return match.replace(/ *\|\|\s*\(!support\.nodeClass[\s\S]+?\)\)/, ''); }); // remove `support.nodeClass` from `_.isEqual` source = source.replace(matchFunction(source, 'isEqual'), function(match) { return match.replace(/ *\|\|\s*\(!support\.nodeClass[\s\S]+?\)\)\)/, ''); }); return source; } /** * Removes all `support.nonEnumArgs` references from `source`. * * @private * @param {String} source The source to process. * @returns {String} Returns the modified source. */ function removeSupportNonEnumArgs(source) { source = removeSupportProp(source, 'nonEnumArgs'); // remove `support.nonEnumArgs` from `_.keys` source = source.replace(matchFunction(source, 'keys'), function(match) { return match .replace(/(?:\s*\|\|\s*)?\(support\.nonEnumArgs[^)]+\)\)/, '') .replace(/\s*if *\(\s*\)[^}]+}/, ''); }); // remove `nonEnumArgs` from `iteratorTemplate` source = source.replace(getIteratorTemplate(source), function(match) { return match .replace(/(?: *\/\/.*\n)*( *["'] *)<% *} *else *if *\(support\.nonEnumArgs[\s\S]+?(\1<% *} *%>.+)/, '$2') .replace(/ *\|\|\s*support\.nonEnumArgs/, ''); }); return source; } /** * Removes all `support.nonEnumShadows` references from `source`. * * @private * @param {String} source The source to process. * @returns {String} Returns the modified source. */ function removeSupportNonEnumShadows(source) { source = removeSupportProp(source, 'nonEnumShadows'); source = removeVar(source, 'shadowedProps'); source = removeFromCreateIterator(source, 'shadowedProps'); // remove `support.nonEnumShadows` from `iteratorTemplate` source = source.replace(getIteratorTemplate(source), function(match) { return match.replace(/(?: *\/\/.*\n)* *["']( *)<% *if *\(support\.nonEnumShadows[\s\S]+?["']\1<% *} *%>.+/, ''); }); return source; } /** * Removes all `support.ownLast` references from `source`. * * @private * @param {String} source The source to process. * @returns {String} Returns the modified source. */ function removeSupportOwnLast(source) { source = removeSupportProp(source, 'ownLast'); // remove `support.ownLast` from `shimIsPlainObject` source = source.replace(matchFunction(source, 'shimIsPlainObject'), function(match) { return match.replace(/(?:\s*\/\/.*)*\n( *)if *\(support\.ownLast[\s\S]+?\n\1}/, ''); }); return source; } /** * Removes all `support.spliceObjects` references from `source`. * * @private * @param {String} source The source to process. * @returns {String} Returns the modified source. */ function removeSupportSpliceObjects(source) { source = removeSupportProp(source, 'spliceObjects'); // remove `support.spliceObjects` fix from the `Array` function mixins source = source.replace(/(?:\s*\/\/.*)*\n( *)if *\(!support\.spliceObjects[\s\S]+?(?:{\s*}|\n\1})/, ''); return source; } /** * Removes all `support.unindexedChars` references from `source`. * * @private * @param {String} source The source to process. * @returns {String} Returns the modified source. */ function removeSupportUnindexedChars(source) { source = removeSupportProp(source, 'unindexedChars'); // remove `support.unindexedChars` from `_.at` source = source.replace(matchFunction(source, 'at'), function(match) { return match.replace(/^ *if *\(support\.unindexedChars[^}]+}\n+/m, ''); }); // remove `support.unindexedChars` from `_.reduceRight` source = source.replace(matchFunction(source, 'reduceRight'), function(match) { return match.replace(/}\s*else if *\(support\.unindexedChars[^}]+/, ''); }); // remove `support.unindexedChars` from `_.toArray` source = source.replace(matchFunction(source, 'toArray'), function(match) { return match.replace(/(return\b).+?support\.unindexedChars[^:]+:\s*/, '$1 '); }); // remove `support.unindexedChars` from `iteratorTemplate` source = source.replace(getIteratorTemplate(source), function(match) { return match .replace(/'if *\(<%= *arrays *%>[^']*/, '$&\\n') .replace(/(?: *\/\/.*\n)* *["']( *)<% *if *\(support\.unindexedChars[\s\S]+?["']\1<% *} *%>.+/, ''); }); return source; } /** * Removes all `runInContext` references from `source`. * * @private * @param {String} source The source to process. * @returns {String} Returns the modified source. */ function removeRunInContext(source) { source = removeVar(source, 'contextProps'); // remove function scaffolding, leaving most of its content source = source.replace(matchFunction(source, 'runInContext'), function(match) { return match .replace(/^[\s\S]+?function runInContext[\s\S]+?context *= *context.+| *return lodash[\s\S]+$/g, '') .replace(/^ {4}/gm, ' '); }); // cleanup adjusted source source = source .replace(/\bcontext\b/g, 'window') .replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var Array *=[\s\S]+?;\n/, '') .replace(/(return *|= *)_([;)])/g, '$1lodash$2') .replace(/^ *var _ *=.+\n+/m, ''); return source; } /** * Removes all `setImmediate` references from `source`. * * @private * @param {String} source The source to process. * @returns {String} Returns the modified source. */ function removeSetImmediate(source) { source = removeVar(source, 'setImmediate'); // remove the `setImmediate` fork of `_.defer`. source = source.replace(/(?:\s*\/\/.*)*\n( *)if *\(isV8 *&& *freeModule[\s\S]+?\n\1}/, ''); return source; } /** * Removes a given property from the `support` object in `source`. * * @private * @param {String} source The source to process. * @param {String} varName The name of the `support` property to remove. * @returns {String} Returns the modified source. */ function removeSupportProp(source, propName) { return source.replace(RegExp( // match multi-line comment block '(?:\\n +/\\*[^*]*\\*+(?:[^/][^*]*\\*+)*/)?\\n' + // match a `try` block '(?: *try\\b.+\\n)?' + // match the `support` property assignment ' *support\\.' + propName + ' *=.+\\n' + // match `catch` block '(?:( *).+?catch\\b[\\s\\S]+?\\n\\1}\\n)?' ), ''); } /** * Removes a given variable from `source`. * * @private * @param {String} source The source to process. * @param {String} varName The name of the variable to remove. * @returns {String} Returns the modified source. */ function removeVar(source, varName) { // simplify complex variable assignments if (/^(?:cloneableClasses|contextProps|ctorByClass|shadowedProps)$/.test(varName)) { source = source.replace(RegExp('(var ' + varName + ' *=)[\\s\\S]+?\\n\\n'), '$1=null;\n\n'); } source = source.replace(RegExp( // match multi-line comment block '(?:\\n +/\\*[^*]*\\*+(?:[^/][^*]*\\*+)*/)?\\n' + // match a variable declaration that's not part of a declaration list '( *)var ' + varName + ' *= *(?:.+?(?:;|&&\\n[^;]+;)|(?:\\w+\\(|{)[\\s\\S]+?\\n\\1.+?;)\\n|' + // match a variable in a declaration list '^ *' + varName + ' *=.+?,\\n', 'm' ), ''); // remove a varaible at the start of a variable declaration list source = source.replace(RegExp('(var +)' + varName + ' *=.+?,\\s+'), '$1'); // remove a variable at the end of a variable declaration list source = source.replace(RegExp(',\\s*' + varName + ' *=.+?;'), ';'); return removeFromCreateIterator(source, varName); } /** * Replaces the `funcName` function body in `source` with `funcValue`. * * @private * @param {String} source The source to process. * @param {String} varName The name of the function to replace. * @returns {String} Returns the modified source. */ function replaceFunction(source, funcName, funcValue) { var snippet = matchFunction(source, funcName); if (!snippet) { return source; } // clip snippet after the JSDoc comment block snippet = snippet.replace(/^\s*(?:\/\/.*|\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)\n/, ''); source = source.replace(snippet, function() { return funcValue .replace(RegExp('^' + getIndent(funcValue), 'gm'), getIndent(snippet)) .trimRight() + '\n'; }); return source; } /** * Replaces the `support` object `propName` property value in `source` with `propValue`. * * @private * @param {String} source The source to process. * @param {String} varName The name of the `support` property to replace. * @returns {String} Returns the modified source. */ function replaceSupportProp(source, propName, propValue) { return source.replace(RegExp( // match a `try` block '(?: *try\\b.+\\n)?' + // match the `support` property assignment '( *support\\.' + propName + ' *=).+\\n' + // match `catch` block '(?:( *).+?catch\\b[\\s\\S]+?\\n\\2}\\n)?' ), function(match, left) { return left + ' ' + propValue + ';\n'; }); } /** * Replaces the `varName` variable declaration value in `source` with `varValue`. * * @private * @param {String} source The source to inspect. * @param {String} varName The name of the variable to replace. * @returns {String} Returns the modified source. */ function replaceVar(source, varName, varValue) { // replace a variable that's not part of a declaration list var result = source.replace(RegExp( '(( *)var ' + varName + ' *=)' + '(?:.+?;|(?:Function\\(.+?|.*?[^,])\\n[\\s\\S]+?\\n\\2.+?;)\\n' ), function(match, left) { return left + ' ' + varValue + ';\n'; }); if (source == result) { // replace a varaible at the start or middle of a declaration list result = source.replace(RegExp('((?:var|\\n) +' + varName + ' *=).+?,'), function(match, left) { return left + ' ' + varValue + ','; }); } if (source == result) { // replace a variable at the end of a variable declaration list result = source.replace(RegExp('(,\\s*' + varName + ' *=).+?;'), function(match, left) { return left + ' ' + varValue + ';'; }); } return result; } /** * Hard-codes the `strict` template option value for `iteratorTemplate`. * * @private * @param {String} source The source to process. * @param {Boolean} value The value to set. * @returns {String} Returns the modified source. */ function setUseStrictOption(source, value) { // inject or remove the "use strict" directive source = source.replace(/^([\s\S]*?function[^{]+{)(?:\s*'use strict';)?/, '$1' + (value ? "\n 'use strict';" : '')); // replace `strict` branch in `iteratorTemplate` with hard-coded option source = source.replace(getIteratorTemplate(source), function(match) { return match.replace(/(template\()(?:\s*"'use strict.+)?/, '$1' + (value ? '\n "\'use strict\';\\n" +' : '')); }); return source; } /*--------------------------------------------------------------------------*/ /** * Creates a debug and/or minified build, executing the `callback` for each. * The `callback` is invoked with two arguments; (filePath, outputSource). * * Note: For a list of commands see `displayHelp()` or run `lodash --help`. * * @param {Array} [options=[]] An array of commands. * @param {Function} callback The function called per build. */ function build(options, callback) { options || (options = []); // the debug version of `source` var debugSource; // used to specify the source map URL var sourceMapURL; // used to report invalid command-line arguments var invalidArgs = _.reject(options.slice(reNode.test(options[0]) ? 2 : 0), function(value, index, options) { if (/^(?:-o|--output)$/.test(options[index - 1]) || /^(?:category|exclude|exports|iife|include|moduleId|minus|plus|settings|template)=.*$/.test(value)) { return true; } var result = [ 'backbone', 'csp', 'legacy', 'mobile', 'modern', 'modularize', 'strict', 'underscore', '-c', '--stdout', '-d', '--debug', '-h', '--help', '-m', '--minify', '-o', '--output', '-p', '--source-map', '-s', '--silent', '-V', '--version' ].indexOf(value) > -1; if (!result && /^(?:-p|--source-map)$/.test(options[index - 1])) { result = true; sourceMapURL = value; } return result; }); // report invalid arguments if (invalidArgs.length) { console.log( '\n' + 'Invalid argument' + (invalidArgs.length > 1 ? 's' : '') + ' passed: ' + invalidArgs.join(', ') ); displayHelp(); return; } // display help message if (_.find(options, function(arg) { return /^(?:-h|--help)$/.test(arg); })) { displayHelp(); return; } // display `lodash.VERSION` if (_.find(options, function(arg) { return /^(?:-V|--version)$/.test(arg); })) { console.log(_.VERSION); return; } /*------------------------------------------------------------------------*/ // backup `dependencyMap` to restore later var dependencyBackup = _.cloneDeep(dependencyMap); // used to specify a custom IIFE to wrap Lo-Dash var iife = options.reduce(function(result, value) { var match = value.match(/iife=(.*)/); return match ? match[1] : result; }, null); // the path to the source file var filePath = path.join(__dirname, 'lodash.js'); // flag to specify a Backbone build var isBackbone = options.indexOf('backbone') > -1; // flag to specify a Content Security Policy build var isCSP = options.indexOf('csp') > -1 || options.indexOf('CSP') > -1; // flag to specify only creating the debug build var isDebug = options.indexOf('-d') > -1 || options.indexOf('--debug') > -1; // flag to indicate that a custom IIFE was specified var isIIFE = typeof iife == 'string'; // flag to specify creating a source map for the minified source var isMapped = options.indexOf('-p') > -1 || options.indexOf('--source-map') > -1; // flag to specify only creating the minified build var isMinify = options.indexOf('-m') > -1 || options.indexOf('--minify') > -1; // flag to specify a mobile build var isMobile = isCSP || options.indexOf('mobile') > -1; // flag to specify a modern build var isModern = isMobile || options.indexOf('modern') > -1; // flag to specify a modularize build var isModularize = options.indexOf('modularize') > -1; // flag to specify writing output to standard output var isStdOut = options.indexOf('-c') > -1 || options.indexOf('--stdout') > -1; // flag to specify skipping status updates normally logged to the console var isSilent = isStdOut || options.indexOf('-s') > -1 || options.indexOf('--silent') > -1; // flag to specify `_.assign`, `_.bindAll`, and `_.defaults` are // constructed using the "use strict" directive var isStrict = options.indexOf('strict') > -1; // flag to specify an Underscore build var isUnderscore = isBackbone || options.indexOf('underscore') > -1; // flag to specify a legacy build var isLegacy = !(isModern || isUnderscore) && options.indexOf('legacy') > -1; // used to specify methods of specific categories var categories = options.reduce(function(result, value) { return /category/.test(value) ? optionToArray(value) : result; }, []); // used to specify the ways to export the `lodash` function var exportsOptions = options.reduce(function(result, value) { return /exports/.test(value) ? optionToArray(value).sort() : result; }, isUnderscore ? ['commonjs', 'global', 'node'] : exportsAll.slice() ); // used to specify the AMD module ID of Lo-Dash used by precompiled templates var moduleId = options.reduce(function(result, value) { var match = value.match(/moduleId=(.*)/); return match ? match[1] : result; }, 'lodash'); // used to specify the output path for builds var outputPath = options.reduce(function(result, value, index) { if (/-o|--output/.test(value)) { result = options[index + 1]; var dirname = path.dirname(result); mkdirpSync(dirname); result = path.join(fs.realpathSync(dirname), path.basename(result)); } return result; }, ''); // used to match external template files to precompile var templatePattern = options.reduce(function(result, value) { var match = value.match(/template=(.+)$/); return match ? path.join(fs.realpathSync(path.dirname(match[1])), path.basename(match[1])) : result; }, ''); // used when precompiling template files var templateSettings = options.reduce(function(result, value) { var match = value.match(/settings=(.+)$/); return match ? _.assign(result, Function('return {' + match[1].replace(/^{|}$/g, '') + '}')()) : result; }, _.assign(_.clone(_.templateSettings), { 'moduleId': moduleId })); // flag to specify a template build var isTemplate = !!templatePattern; // the lodash.js source var source = fs.readFileSync(filePath, 'utf8'); // flag to specify replacing Lo-Dash's `_.clone` with Underscore's var useUnderscoreClone = isUnderscore; // flags to specify exposing Lo-Dash methods in an Underscore build var exposeAssign = !isUnderscore, exposeCreateCallback = !isUnderscore, exposeForIn = !isUnderscore, exposeForOwn = !isUnderscore, exposeIsPlainObject = !isUnderscore, exposeZipObject = !isUnderscore; // flags to specify export options var isAMD = exportsOptions.indexOf('amd') > -1, isCommonJS = exportsOptions.indexOf('commonjs') > -1, isGlobal = exportsOptions.indexOf('global') > -1, isNode = exportsOptions.indexOf('node') > -1; /*------------------------------------------------------------------------*/ // names of methods to include in the build var buildMethods = !isTemplate && (function() { var result; var includeMethods = options.reduce(function(accumulator, value) { return /include/.test(value) ? _.union(accumulator, optionToMethodsArray(source, value)) : accumulator; }, []); var minusMethods = options.reduce(function(accumulator, value) { return /exclude|minus/.test(value) ? _.union(accumulator, optionToMethodsArray(source, value)) : accumulator; }, []); var plusMethods = options.reduce(function(accumulator, value) { return /plus/.test(value) ? _.union(accumulator, optionToMethodsArray(source, value)) : accumulator; }, []); // set flags to include Lo-Dash's methods if explicitly requested if (isUnderscore) { var methods = _.without.apply(_, [_.union(includeMethods, plusMethods)].concat(minusMethods)); exposeAssign = methods.indexOf('assign') > -1; exposeCreateCallback = methods.indexOf('createCallback') > -1; exposeForIn = methods.indexOf('forIn') > -1; exposeForOwn = methods.indexOf('forOwn') > -1; exposeIsPlainObject = methods.indexOf('isPlainObject') > -1; exposeZipObject = methods.indexOf('zipObject') > -1; methods = _.without.apply(_, [plusMethods].concat(minusMethods)); useUnderscoreClone = methods.indexOf('clone') < 0; } // update dependencies if (isLegacy) { dependencyMap.defer = _.without(dependencyMap.defer, 'bind'); } if (isModern) { dependencyMap.isEmpty = _.without(dependencyMap.isEmpty, 'isArguments'); dependencyMap.isEqual = _.without(dependencyMap.isEqual, 'isArguments'); dependencyMap.keys = _.without(dependencyMap.keys, 'isArguments'); dependencyMap.reduceRight = _.without(dependencyMap.reduceRight, 'isString'); } if (isUnderscore) { dependencyMap.contains = _.without(dependencyMap.contains, 'isString'); dependencyMap.flatten = _.without(dependencyMap.flatten, 'createCallback'); dependencyMap.isEmpty = ['isArray', 'isString']; dependencyMap.isEqual = _.without(dependencyMap.isEqual, 'forIn', 'isArguments'); dependencyMap.max = _.without(dependencyMap.max, 'isString'); dependencyMap.min = _.without(dependencyMap.min, 'isString'); dependencyMap.pick = _.without(dependencyMap.pick, 'forIn', 'isObject'); dependencyMap.reduceRight = _.without(dependencyMap.reduceRight, 'isString'); dependencyMap.template = _.without(dependencyMap.template, 'keys', 'values'); dependencyMap.where.push('find', 'isEmpty'); if (useUnderscoreClone) { dependencyMap.clone = _.without(dependencyMap.clone, 'forEach', 'forOwn'); } } if (isModern || isUnderscore) { dependencyMap.at = _.without(dependencyMap.at, 'isString'); dependencyMap.forEach = _.without(dependencyMap.forEach, 'isArguments', 'isString'); dependencyMap.forIn = _.without(dependencyMap.forIn, 'isArguments'); dependencyMap.forOwn = _.without(dependencyMap.forOwn, 'isArguments'); dependencyMap.toArray = _.without(dependencyMap.toArray, 'isString'); } // add method names explicitly if (includeMethods.length) { result = getDependencies(includeMethods); } // add method names required by Backbone and Underscore builds if (isBackbone && !result) { result = getDependencies(backboneDependencies); } else if (isUnderscore && !result) { result = getDependencies(underscoreMethods); } // add method names by category if (categories.length) { result = _.union(result || [], getDependencies(categories.reduce(function(accumulator, category) { // resolve method names belonging to each category (case-insensitive) return accumulator.concat(getMethodsByCategory(source, capitalize(category))); }, []))); } if (!result) { result = allMethods.slice(); } if (plusMethods.length) { result = _.union(result, getDependencies(plusMethods)); } if (minusMethods.length) { result = _.without.apply(_, [result].concat(minusMethods, getDependants(minusMethods))); } return result; }()); /*------------------------------------------------------------------------*/ // load customized Lo-Dash module var lodash = !isTemplate && (function() { var context = vm.createContext({ 'clearTimeout': clearTimeout, 'console': console, 'setTimeout': setTimeout }); source = setUseStrictOption(source, isStrict); if (isLegacy) { _.each(['getPrototypeOf', 'nativeBind', 'nativeIsArray', 'nativeKeys'], function(varName) { source = replaceVar(source, varName, 'false'); }); _.each(['argsClass', 'fastBind', 'fastKeys'], function(propName) { source = replaceSupportProp(source, propName, 'false'); }); source = removeKeysOptimization(source); } if (isMobile || isUnderscore) { source = removeKeysOptimization(source); source = removeSetImmediate(source); } if (isModern || isUnderscore) { source = removeSupportNonEnumShadows(source); source = removeSupportEnumPrototypes(source); source = removeSupportOwnLast(source); source = removeSupportUnindexedChars(source); source = removeSupportNodeClass(source); if (!isMobile) { source = removeSupportNonEnumArgs(source); } } if (isModern) { // remove `_.isPlainObject` fallback source = source.replace(matchFunction(source, 'isPlainObject'), function(match) { return match.replace(/!getPrototypeOf[^:]+:\s*/, ''); }); if (!isMobile) { source = removeIsFunctionFallback(source); } } if (isUnderscore) { // replace `_.assign` source = replaceFunction(source, 'assign', [ 'function assign(object) {', ' if (!object) {', ' return object;', ' }', ' for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {', ' var iterable = arguments[argsIndex];', ' if (iterable) {', ' for (var key in iterable) {', ' object[key] = iterable[key];', ' }', ' }', ' }', ' return object;', '}' ].join('\n')); // replace `_.clone` if (useUnderscoreClone) { source = replaceFunction(source, 'clone', [ 'function clone(value) {', ' return isObject(value)', ' ? (isArray(value) ? slice(value) : assign({}, value))', ' : value', '}' ].join('\n')); } // replace `_.contains` source = replaceFunction(source, 'contains', [ 'function contains(collection, target) {', ' var length = collection ? collection.length : 0,', ' result = false;', " if (typeof length == 'number') {", ' result = indexOf(collection, target) > -1;', ' } else {', ' each(collection, function(value) {', ' return (result = value === target) && indicatorObject;', ' });', ' }', ' return result;', '}' ].join('\n')); // replace `_.defaults` source = replaceFunction(source, 'defaults', [ 'function defaults(object) {', ' if (!object) {', ' return object;', ' }', ' for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {', ' var iterable = arguments[argsIndex];', ' if (iterable) {', ' for (var key in iterable) {', ' if (object[key] == null) {', ' object[key] = iterable[key];', ' }', ' }', ' }', ' }', ' return object;', '}' ].join('\n')); // replace `_.difference` source = replaceFunction(source, 'difference', [ 'function difference(array) {', ' var index = -1,', ' length = array.length,', ' flattened = concat.apply(arrayRef, arguments),', ' result = [];', '', ' while (++index < length) {', ' var value = array[index]', ' if (indexOf(flattened, value, length) < 0) {', ' result.push(value);', ' }', ' }', ' return result;', '}' ].join('\n')); // replace `_.flatten` source = replaceFunction(source, 'flatten', [ 'function flatten(array, isShallow) {', ' var index = -1,', ' length = array ? array.length : 0,', ' result = [];', '' , ' while (++index < length) {', ' var value = array[index];', ' if (isArray(value)) {', ' push.apply(result, isShallow ? value : flatten(value));', ' } else {', ' result.push(value);', ' }', ' }', ' return result;', '}' ].join('\n')); // replace `_.intersection` source = replaceFunction(source, 'intersection', [ 'function intersection(array) {', ' var args = arguments,', ' argsLength = args.length,', ' index = -1,', ' length = array ? array.length : 0,', ' result = [];', '', ' outer:', ' while (++index < length) {', ' var value = array[index];', ' if (indexOf(result, value) < 0) {', ' var argsIndex = argsLength;', ' while (--argsIndex) {', ' if (indexOf(args[argsIndex], value) < 0) {', ' continue outer;', ' }', ' }', ' result.push(value);', ' }', ' }', ' return result;', '}' ].join('\n')); // replace `_.isEmpty` source = replaceFunction(source, 'isEmpty', [ 'function isEmpty(value) {', ' if (!value) {', ' return true;', ' }', ' if (isArray(value) || isString(value)) {', ' return !value.length;', ' }', ' for (var key in value) {', ' if (hasOwnProperty.call(value, key)) {', ' return false;', ' }', ' }', ' return true;', '}' ].join('\n')); // replace `_.isEqual` source = replaceFunction(source, 'isEqual', [ 'function isEqual(a, b, stackA, stackB) {', ' if (a === b) {', ' return a !== 0 || (1 / a == 1 / b);', ' }', ' var type = typeof a,', ' otherType = typeof b;', '', ' if (a === a &&', " (!a || (type != 'function' && type != 'object')) &&", " (!b || (otherType != 'function' && otherType != 'object'))) {", ' return false;', ' }', ' if (a == null || b == null) {', ' return a === b;', ' }', ' var className = toString.call(a),', ' otherClass = toString.call(b);', '', ' if (className != otherClass) {', ' return false;', ' }', ' switch (className) {', ' case boolClass:', ' case dateClass:', ' return +a == +b;', '', ' case numberClass:', ' return a != +a', ' ? b != +b', ' : (a == 0 ? (1 / a == 1 / b) : a == +b);', '', ' case regexpClass:', ' case stringClass:', " return a == b + '';", ' }', ' var isArr = className == arrayClass;', ' if (!isArr) {', ' if (a instanceof lodash || b instanceof lodash) {', ' return isEqual(a.__wrapped__ || a, b.__wrapped__ || b, stackA, stackB);', ' }', ' if (className != objectClass) {', ' return false;', ' }', ' var ctorA = a.constructor,', ' ctorB = b.constructor;', '', ' if (ctorA != ctorB && !(', ' isFunction(ctorA) && ctorA instanceof ctorA &&', ' isFunction(ctorB) && ctorB instanceof ctorB', ' )) {', ' return false;', ' }', ' }', ' stackA || (stackA = []);', ' stackB || (stackB = []);', '', ' var length = stackA.length;', ' while (length--) {', ' if (stackA[length] == a) {', ' return stackB[length] == b;', ' }', ' }', ' var result = true,', ' size = 0;', '', ' stackA.push(a);', ' stackB.push(b);', '', ' if (isArr) {', ' size = b.length;', ' result = size == a.length;', '', ' if (result) {', ' while (size--) {', ' if (!(result = isEqual(a[size], b[size], stackA, stackB))) {', ' break;', ' }', ' }', ' }', ' return result;', ' }', ' forIn(b, function(value, key, b) {', ' if (hasOwnProperty.call(b, key)) {', ' size++;', ' return !(result = hasOwnProperty.call(a, key) && isEqual(a[key], value, stackA, stackB)) && indicatorObject;', ' }', ' });', '', ' if (result) {', ' forIn(a, function(value, key, a) {', ' if (hasOwnProperty.call(a, key)) {', ' return !(result = --size > -1) && indicatorObject;', ' }', ' });', ' }', ' return result;', '}' ].join('\n')); // replace `lodash` source = replaceFunction(source, 'lodash', [ 'function lodash(value) {', ' return (value instanceof lodash)', ' ? value', ' : new lodashWrapper(value)', '}' ].join('\n')); // replace `_.omit` source = replaceFunction(source, 'omit', [ 'function omit(object) {', ' var props = concat.apply(arrayRef, arguments),', ' result = {};', '', ' forIn(object, function(value, key) {', ' if (indexOf(props, key, 1) < 0) {', ' result[key] = value;', ' }', ' });', ' return result;', '}' ].join('\n')); // replace `_.pick` source = replaceFunction(source, 'pick', [ 'function pick(object) {', ' var index = 0,', ' props = concat.apply(arrayRef, arguments),', ' length = props.length,', ' result = {};', '', ' while (++index < length) {', ' var prop = props[index];', ' if (prop in object) {', ' result[prop] = object[prop];', ' }', ' }', ' return result;', '}' ].join('\n')); // replace `_.result` source = replaceFunction(source, 'result', [ 'function result(object, property) {', ' var value = object ? object[property] : null;', ' return isFunction(value) ? object[property]() : value;', '}' ].join('\n')); // replace `_.template` source = replaceFunction(source, 'template', [ 'function template(text, data, options) {', " text || (text = '');", ' options = defaults({}, options, lodash.templateSettings);', '', ' var index = 0,', ' source = "__p += \'",', ' variable = options.variable;', '', ' var reDelimiters = RegExp(', " (options.escape || reNoMatch).source + '|' +", " (options.interpolate || reNoMatch).source + '|' +", " (options.evaluate || reNoMatch).source + '|$'", " , 'g');", '', ' text.replace(reDelimiters, function(match, escapeValue, interpolateValue, evaluateValue, offset) {', ' source += text.slice(index, offset).replace(reUnescapedString, escapeStringChar);', ' if (escapeValue) {', ' source += "\' +\\n_.escape(" + escapeValue + ") +\\n\'";', ' }', ' if (evaluateValue) {', ' source += "\';\\n" + evaluateValue + ";\\n__p += \'";', ' }', ' if (interpolateValue) {', ' source += "\' +\\n((__t = (" + interpolateValue + ")) == null ? \'\' : __t) +\\n\'";', ' }', ' index = offset + match.length;', ' return match;', ' });', '', ' source += "\';\\n";', ' if (!variable) {', " variable = 'obj';", " source = 'with (' + variable + ' || {}) {\\n' + source + '\\n}\\n';", ' }', " source = 'function(' + variable + ') {\\n' +", ' "var __t, __p = \'\', __j = Array.prototype.join;\\n" +', ' "function print() { __p += __j.call(arguments, \'\') }\\n" +', ' source +', " 'return __p\\n}';", '', ' try {', " var result = Function('_', 'return ' + source)(lodash);", ' } catch(e) {', ' e.source = source;', ' throw e;', ' }', ' if (data) {', ' return result(data);', ' }', ' result.source = source;', ' return result;', '}' ].join('\n')); // replace `_.times` source = replaceFunction(source, 'times', [ 'function times(n, callback, thisArg) {', ' var index = -1,', ' result = Array(n > -1 ? n : 0);', '', ' while (++index < n) {', ' result[index] = callback.call(thisArg, index);', ' }', ' return result;', '}' ].join('\n')); // replace `_.uniq` source = replaceFunction(source, 'uniq', [ 'function uniq(array, isSorted, callback, thisArg) {', ' var index = -1,', ' length = array ? array.length : 0,', ' result = [],', ' seen = result;', '', " if (typeof isSorted != 'boolean' && isSorted != null) {", ' thisArg = callback;', ' callback = isSorted;', ' isSorted = false;', ' }', ' if (callback != null) {', ' seen = [];', ' callback = createCallback(callback, thisArg);', ' }', ' while (++index < length) {', ' var value = array[index],', ' computed = callback ? callback(value, index, array) : value;', '', ' if (isSorted', ' ? !index || seen[seen.length - 1] !== computed', ' : indexOf(seen, computed) < 0', ' ) {', ' if (callback) {', ' seen.push(computed);', ' }', ' result.push(value);', ' }', ' }', ' return result;', '}' ].join('\n')); // replace `_.uniqueId` source = replaceFunction(source, 'uniqueId', [ 'function uniqueId(prefix) {', " var id = ++idCounter + '';", ' return prefix ? prefix + id : id;', '}' ].join('\n')); // replace `_.where` source = replaceFunction(source, 'where', [ 'function where(collection, properties, first) {', ' return (first && isEmpty(properties))', ' ? null', ' : (first ? find : filter)(collection, properties);', '}' ].join('\n')); // replace `_.without` source = replaceFunction(source, 'without', [ 'function without(array) {', ' var index = -1,', ' length = array.length,', ' result = [];', '', ' while (++index < length) {', ' var value = array[index]', ' if (indexOf(arguments, value, 1) < 0) {', ' result.push(value);', ' }', ' }', ' return result', '}' ].join('\n')); // add `_.findWhere` source = source.replace(matchFunction(source, 'find'), function(match) { var indent = getIndent(match); return match && (match + [ '', 'function findWhere(object, properties) {', ' return where(object, properties, true);', '}', '' ].join('\n' + indent)); }); source = source.replace(getMethodAssignments(source), function(match) { 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(/\bisEqual\(([^,]+), *([^,]+)[^)]+\)/, '$1 === $2'); }); // remove conditional `charCodeCallback` use from `_.max` and `_.min` _.each(['max', 'min'], function(methodName) { source = source.replace(matchFunction(source, methodName), function(match) { return match.replace(/=.+?callback *&& *isString[^:]+:\s*/g, '= '); }); }); // replace `lodash.createCallback` references with `createCallback` if (!exposeCreateCallback) { source = source.replace(/\blodash\.(createCallback\()\b/g, '$1'); } // 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) { return match .replace(/, *right[^)]*/, '') .replace(/(function createBound\([^{]+{)[\s\S]+?(\n *function bound)/, '$1$2') .replace(/thisBinding *=[^}]+}/, 'thisBinding = thisArg;\n') .replace(/\(args *=.+/, 'partialArgs.concat(slice(args))'); }); } } vm.runInContext(source, context); return context._; }()); /*------------------------------------------------------------------------*/ if (isTemplate) { source = buildTemplate(templatePattern, templateSettings); } else { // remove methods from the build allMethods.forEach(function(otherName) { if (!_.contains(buildMethods, otherName)) { source = removeFunction(source, otherName); } }); // remove `isArguments` fallback before `isArguments` is transformed by // other parts of the build process if (isRemoved(source, 'isArguments')) { source = removeIsArgumentsFallback(source); } // remove `iteratorTemplate` dependency checks from `_.template` source = source.replace(matchFunction(source, 'template'), function(match) { return match .replace(/iteratorTemplate *&& */g, '') .replace(/iteratorTemplate *\? *([^:]+?) *:[^,;]+/g, '$1'); }); /*----------------------------------------------------------------------*/ if (isLegacy) { source = removeSetImmediate(source); source = removeSupportProp(source, 'fastBind'); _.each(['isIeOpera', 'isV8', 'nativeBind', 'nativeIsArray', 'nativeKeys', 'reNative'], function(varName) { source = removeVar(source, varName); }); // remove native `Function#bind` branch in `_.bind` source = source.replace(matchFunction(source, 'bind'), function(match) { return match.replace(/(?:\s*\/\/.*)*\s*return support\.fastBind[^:]+:\s*/, 'return '); }); // remove native `Array.isArray` branch in `_.isArray` source = source.replace(matchFunction(source, 'isArray'), function(match) { return match.replace(/nativeIsArray * \|\|\s*/, ''); }); // replace `_.keys` with `shimKeys` if (!isRemoved(source, 'keys')) { source = source.replace( matchFunction(source, 'keys').replace(/[\s\S]+?var keys *= */, ''), matchFunction(source, 'shimKeys').replace(/[\s\S]+?function shimKeys/, 'function').replace(/}\n$/, '};\n') ); source = removeFunction(source, 'shimKeys'); } // replace `_.isArguments` with fallback if (!isRemoved(source, 'isArguments')) { source = source.replace( matchFunction(source, 'isArguments').replace(/[\s\S]+?function isArguments/, ''), getIsArgumentsFallback(source).match(/isArguments *= *function([\s\S]+?) *};/)[1] + ' }\n' ); source = removeIsArgumentsFallback(source); } } if (isModern) { source = removeSupportArgsObject(source); source = removeSupportSpliceObjects(source); source = removeIsArgumentsFallback(source); } if (isModern || isUnderscore) { source = removeSupportArgsClass(source); source = removeSupportNodeClass(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 var reFunc = RegExp('^( *)(var ' + methodName.replace(/^_/, '') + ' *= *)createIterator\\(((?:{|[a-zA-Z])[\\s\\S]+?)\\);\\n', 'm'); if (reFunc.test(source)) { // extract, format, and inject the compiled function's source code source = source.replace(reFunc, function(match, indent, left) { return (indent + left) + getFunctionSource(lodash[methodName], indent) + ';\n'; }); } }); } if (isUnderscore) { // remove `_.assign`, `_.forIn`, `_.forOwn`, `_.isPlainObject`, and `_.zipObject` assignments (function() { var snippet = getMethodAssignments(source), modified = snippet; if (!exposeAssign) { modified = modified.replace(/^(?: *\/\/.*\s*)* *lodash\.assign *=.+\n/m, ''); } if (!exposeCreateCallback) { modified = modified.replace(/^(?: *\/\/.*\s*)* *lodash\.createCallback *=.+\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, ''); } if (!exposeZipObject) { modified = modified.replace(/^(?: *\/\/.*\s*)* *lodash\.zipObject *=.+\n/m, ''); } source = source.replace(snippet, function() { return modified; }); }()); // unexpose `lodash.support` source = source.replace(/lodash\.support *= */, ''); // remove `thisArg` from unexposed `forIn` and `forOwn` _.each([ { 'methodName': 'forIn', 'flag': exposeForIn }, { 'methodName': 'forOwn', 'flag': exposeForOwn } ], function(data) { if (!data.flag) { source = source.replace(matchFunction(source, data.methodName), function(match) { return match .replace(/(callback), *thisArg/g, '$1') .replace(/^( *)callback *=.+/m, '$1callback || (callback = identity);') }); } }); // 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;'); }); } if (!(isMobile || isUnderscore)) { source = removeFromCreateIterator(source, 'support'); // inline `iteratorTemplate` template source = source.replace(getIteratorTemplate(source), function(match) { var indent = getIndent(match), snippet = getFunctionSource(lodash._iteratorTemplate, indent); // prepend data object references to property names to avoid having to // use a with-statement iteratorOptions.forEach(function(property) { snippet = snippet.replace(RegExp('([^\\w.])\\b' + property + '\\b', 'g'), '$1obj.' + property); }); // remove unnecessary code snippet = snippet .replace(/var __t.+/, "var __p = '';") .replace(/function print[^}]+}/, '') .replace(/'(?:\\n|\s)+'/g, "''") .replace(/__p *\+= *' *';/g, '') .replace(/\s*\+\s*'';/g, ';') .replace(/(__p *\+= *)' *' *\+/g, '$1') .replace(/(?:; *)([{}])|([{}])(?: *;)/g, '$1$2') .replace(/\(\(__t *= *\( *([^)]+?) *\)\) *== *null *\? *'' *: *__t\)/g, '($1)'); // remove the with-statement snippet = snippet.replace(/ *with *\(.+?\) *{/, '\n').replace(/}([^}]*}[^}]*$)/, '$1'); // minor cleanup snippet = snippet .replace(/obj *\|\|\s*\(obj *= *{}\);/, '') .replace(/var __p = '';\s*__p \+=/, 'var __p ='); // remove comments, including sourceURLs snippet = snippet.replace(/\s*\/\/.*(?:\n|$)/g, ''); return indent + 'var iteratorTemplate = ' + snippet + ';\n'; }); } } /*------------------------------------------------------------------------*/ // customize Lo-Dash's IIFE (function() { if (isIIFE) { var token = '%output%', index = iife.indexOf(token); source = source.match(/^\/\**[\s\S]+?\*\/\n/) + iife.slice(0, index) + source.replace(/^[\s\S]+?\(function[^{]+?{|}\(this\)\)[;\s]*$/g, '') + iife.slice(index + token.length); } }()); /*------------------------------------------------------------------------*/ // customize Lo-Dash's export bootstrap (function() { if (!isAMD) { source = source.replace(/(?: *\/\/.*\n)*( *)if *\(typeof +define[\s\S]+?else /, '$1'); } if (!isNode) { source = source.replace(/(?: *\/\/.*\n)*( *)if *\(freeModule[\s\S]+?else *{([\s\S]+?\n)\1}\n+/, '$1$2'); } if (!isCommonJS) { source = source.replace(/(?: *\/\/.*\n)*(?:( *)else *{)?\s*freeExports\.\w+ *=[\s\S]+?(?:\n\1})?\n+/, ''); } if (!isGlobal) { source = source.replace(/(?:( *)(})? *else(?: *if *\(_\))? *{)?(?:\s*\/\/.*)*\s*(?:window\._|_\.templates) *=[\s\S]+?(?:\n\1})?\n+/g, '$1$2\n'); } // remove `if (freeExports) {...}` if it's empty if (isAMD && isGlobal) { source = source.replace(/(?: *\/\/.*\n)* *(?:else )?if *\(freeExports.*?\) *{\s*}\n+/, ''); } else { source = source.replace(/(?: *\/\/.*\n)* *(?:else )?if *\(freeExports.*?\) *{\s*}(?:\s*else *{([\s\S]+?) *})?\n+/, '$1\n'); } }()); /*------------------------------------------------------------------------*/ if (!isTemplate) { // modify/remove references to removed methods/variables if (isRemoved(source, 'invert')) { source = replaceVar(source, 'htmlUnescapes', "{'&':'&','<':'<','>':'>','"':'\"',''':\"'\"}"); } if (isRemoved(source, 'isArguments')) { source = replaceVar(source, 'noArgsClass', 'false'); } if (isRemoved(source, 'isFunction')) { source = removeIsFunctionFallback(source); } if (isRemoved(source, 'mixin')) { // inline `_.mixin` call to ensure proper chaining behavior source = source.replace(/^( *)mixin\(lodash\).+/m, function(match, indent) { return indent + [ 'forOwn(lodash, function(func, methodName) {', ' lodash[methodName] = func;', '', ' lodash.prototype[methodName] = function() {', ' var value = this.__wrapped__,', ' args = [value];', '', ' push.apply(args, arguments);', ' var result = func.apply(lodash, args);', " return (value && typeof value == 'object' && value == result)", ' ? this', ' : new lodashWrapper(result);', ' };', '});' ].join('\n' + indent); }); } if (isRemoved(source, 'value')) { source = removeSupportSpliceObjects(source); source = removeLodashWrapper(source); // simplify the `lodash` function source = replaceFunction(source, 'lodash', [ 'function lodash() {', ' // no operation performed', '}' ].join('\n')); // remove `lodash.prototype` method assignments from `_.mixin` source = replaceFunction(source, 'mixin', [ 'function mixin(object) {', ' forEach(functions(object), function(methodName) {', ' lodash[methodName] = object[methodName];', ' });', '}' ].join('\n')); // remove all `lodash.prototype` additions source = source .replace(/(?:\s*\/\/.*)*\n( *)forOwn\(lodash, *function\(func, *methodName\)[\s\S]+?\n\1}.+/g, '') .replace(/(?:\s*\/\/.*)*\n( *)each\(\['[\s\S]+?\n\1}.+/g, '') .replace(/(?:\s*\/\/.*)*\n *lodash\.prototype.+/g, ''); } // remove functions, variables, and snippets that the minifier may miss if (isRemoved(source, 'clone')) { source = removeVar(source, 'cloneableClasses'); source = removeVar(source, 'ctorByClass'); } if (isRemoved(source, 'defer')) { source = removeSetImmediate(source); } if (isRemoved(source, 'isArray')) { source = removeVar(source, 'nativeIsArray'); } if (isRemoved(source, 'isPlainObject')) { source = removeVar(source, 'getPrototypeOf'); source = removeSupportOwnLast(source); } if (isRemoved(source, 'keys')) { source = removeFunction(source, 'shimKeys'); } if (isRemoved(source, 'parseInt')) { source = removeVar(source, 'nativeParseInt'); } if (isRemoved(source, 'template')) { // remove `templateSettings` assignment source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *lodash\.templateSettings[\s\S]+?};\n/, ''); } if (isRemoved(source, 'isArguments', 'isEmpty')) { source = removeSupportArgsClass(source); } if (isRemoved(source, 'clone', 'isEqual', 'isPlainObject')) { source = removeSupportNodeClass(source); } if ((source.match(/\bcreateIterator\b/g) || []).length < 2) { source = removeFunction(source, 'createIterator'); source = removeVar(source, 'defaultsIteratorOptions'); source = removeVar(source, 'eachIteratorOptions'); source = removeVar(source, 'forOwnIteratorOptions'); source = removeVar(source, 'templateIterator'); source = removeSupportNonEnumShadows(source); source = removeSupportEnumPrototypes(source); } if (isRemoved(source, 'createIterator', 'bind', 'keys')) { source = removeSupportProp(source, 'fastBind'); source = removeVar(source, 'isV8'); source = removeVar(source, 'nativeBind'); } if (isRemoved(source, 'createIterator', 'keys')) { source = removeVar(source, 'nativeKeys'); source = removeKeysOptimization(source); source = removeSupportNonEnumArgs(source); } if (!/support\.(?:enumPrototypes|nonEnumShadows|ownLast)\b/.test(source)) { // remove code used to resolve unneeded `support` properties source = source.replace(/^ *\(function[\s\S]+?\n(( *)var ctor *= *function[\s\S]+?\n *for.+\n)([\s\S]+?)}\(1\)\);\n/m, function(match, setup, indent, body) { if (/support\.spliceObjects\b/.test(match)) { return match.replace(setup, indent + "var object = { '0': 1, 'length': 1 };\n"); } else if (/support\.nonEnumArgs\b/.test(match)) { return match.replace(setup, ''); } return body.replace(RegExp('^' + indent, 'gm'), indent.slice(0, -2)); }); } } if (_.size(source.match(/\bfreeModule\b/g)) < 2) { source = removeVar(source, 'freeModule'); } if (_.size(source.match(/\bfreeExports\b/g)) < 2) { source = removeVar(source, 'freeExports'); } debugSource = cleanupSource(source); source = cleanupSource(source); /*------------------------------------------------------------------------*/ // flag to track if `outputPath` has been used by `callback` var outputUsed = false; // flag to specify creating a custom build var isCustom = ( isLegacy || isMapped || isModern || isStrict || isUnderscore || outputPath || /(?:category|exclude|exports|iife|include|minus|plus)=/.test(options) || !_.isEqual(exportsOptions, exportsAll) ); // used as the basename of the output path var basename = outputPath ? path.basename(outputPath, '.js') : 'lodash' + (isTemplate ? '.template' : isCustom ? '.custom' : ''); // restore `dependencyMap` dependencyMap = dependencyBackup; // output debug build if (!isMinify && (isCustom || isDebug || isTemplate)) { if (isCustom) { debugSource = addCommandsToHeader(debugSource, options); } if (isDebug && isStdOut) { stdout.write(debugSource); callback({ 'source': debugSource }); } else if (!isStdOut) { filePath = outputPath || path.join(cwd, basename + '.js'); outputUsed = true; callback({ 'source': debugSource, 'outputPath': filePath }); } } // begin the minification process if (!isDebug) { if (outputPath && outputUsed) { 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, 'isSilent': isSilent, 'isTemplate': isTemplate, 'modes': isIIFE && ['simple', 'hybrid'], 'outputPath': outputPath, 'sourceMapURL': sourceMapURL, 'onComplete': function(data) { if (isCustom) { data.source = addCommandsToHeader(data.source, options); } if (isStdOut) { stdout.write(data.source); callback(data); } else { callback(data); } } }); } } /*--------------------------------------------------------------------------*/ // expose `build` if (module != require.main) { module.exports = build; } else { // or invoked directly build(process.argv, function(data) { var outputPath = data.outputPath, sourceMap = data.sourceMap; if (outputPath) { fs.writeFileSync(outputPath, data.source, 'utf8'); if (sourceMap) { fs.writeFileSync(path.join(path.dirname(outputPath), path.basename(outputPath, '.js') + '.map'), sourceMap, 'utf8'); } } }); } }());