mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-01-31 15:27:50 +00:00
411 lines
13 KiB
JavaScript
Executable File
411 lines
13 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
;(function() {
|
|
'use strict';
|
|
|
|
/** The Node filesystem and path modules */
|
|
var fs = require('fs'),
|
|
path = require('path');
|
|
|
|
/** Load other modules */
|
|
var lodash = require(path.join(__dirname, 'lodash')),
|
|
minify = require(path.join(__dirname, 'build', 'minify'));
|
|
|
|
/** Flag used to specify a custom build */
|
|
var isCustom = false;
|
|
|
|
/** The lodash.js source */
|
|
var source = fs.readFileSync(path.join(__dirname, 'lodash.js'), 'utf8');
|
|
|
|
/** Used to associate aliases with their real names */
|
|
var aliasToRealMap = {
|
|
'all': 'every',
|
|
'any': 'some',
|
|
'collect': 'map',
|
|
'detect': 'find',
|
|
'each': 'forEach',
|
|
'foldl': 'reduce',
|
|
'foldr': 'reduceRight',
|
|
'head': 'first',
|
|
'include': 'contains',
|
|
'inject': 'reduce',
|
|
'intersect': 'intersection',
|
|
'methods': 'functions',
|
|
'select': 'filter',
|
|
'tail': 'rest',
|
|
'take': 'first',
|
|
'unique': 'uniq'
|
|
};
|
|
|
|
/** Used to associate real names with their aliases */
|
|
var realToAliasMap = {
|
|
'contains': ['include'],
|
|
'every': ['all'],
|
|
'filter': ['select'],
|
|
'find': ['detect'],
|
|
'first': ['head', 'take'],
|
|
'forEach': ['each'],
|
|
'functions': ['methods'],
|
|
'intersection': ['intersect'],
|
|
'map': ['collect'],
|
|
'reduce': ['foldl', 'inject'],
|
|
'reduceRight': ['foldr'],
|
|
'rest': ['tail'],
|
|
'some': ['any'],
|
|
'uniq': ['unique']
|
|
};
|
|
|
|
/** Used to track function dependencies */
|
|
var dependencyMap = {
|
|
'after': [],
|
|
'bind': [],
|
|
'bindAll': ['bind'],
|
|
'chain': [],
|
|
'clone': ['extend', 'isArray'],
|
|
'compact': [],
|
|
'compose': [],
|
|
'contains': ['createIterator'],
|
|
'createIterator': ['template'],
|
|
'debounce': [],
|
|
'defaults': ['createIterator'],
|
|
'defer': [],
|
|
'delay': [],
|
|
'difference': ['indexOf'],
|
|
'escape': [],
|
|
'every': ['bind', 'createIterator', 'identity'],
|
|
'extend': ['createIterator'],
|
|
'filter': ['bind', 'createIterator', 'identity'],
|
|
'find': ['createIterator'],
|
|
'first': [],
|
|
'flatten': ['isArray'],
|
|
'forEach': ['bind', 'createIterator'],
|
|
'functions': ['createIterator'],
|
|
'groupBy': ['bind', 'createIterator'],
|
|
'has': [],
|
|
'identity': [],
|
|
'indexOf': ['sortedIndex'],
|
|
'initial': [],
|
|
'intersection': ['every', 'indexOf'],
|
|
'invoke': [],
|
|
'isArguments': [],
|
|
'isArray': [],
|
|
'isBoolean': [],
|
|
'isDate': [],
|
|
'isElement': [],
|
|
'isEmpty': ['createIterator'],
|
|
'isEqual': [],
|
|
'isFinite': [],
|
|
'isFunction': [],
|
|
'isNaN': [],
|
|
'isNull': [],
|
|
'isNumber': [],
|
|
'isObject': [],
|
|
'isRegExp': [],
|
|
'isString': [],
|
|
'isUndefined': [],
|
|
'keys': ['createIterator'],
|
|
'last': [],
|
|
'lastIndexOf': [],
|
|
'map': ['bind', 'createIterator', 'identity'],
|
|
'max': ['bind'],
|
|
'memoize': [],
|
|
'min': ['bind'],
|
|
'mixin': ['forEach'],
|
|
'noConflict': [],
|
|
'once': [],
|
|
'partial': [],
|
|
'pick': [],
|
|
'pluck': ['createIterator'],
|
|
'range': [],
|
|
'reduce': ['bind', 'createIterator'],
|
|
'reduceRight': ['bind', 'keys'],
|
|
'reject': ['bind', 'createIterator', 'identity'],
|
|
'rest': [],
|
|
'result': [],
|
|
'shuffle': [],
|
|
'size': ['keys'],
|
|
'some': ['bind', 'createIterator', 'identity'],
|
|
'sortBy': ['bind', 'map', 'pluck'],
|
|
'sortedIndex': [],
|
|
'tap': [],
|
|
'template': ['escape'],
|
|
'throttle': [],
|
|
'times': ['bind'],
|
|
'toArray': ['values'],
|
|
'union': ['indexOf'],
|
|
'uniq': ['indexOf'],
|
|
'uniqueId': [],
|
|
'values': ['createIterator'],
|
|
'without': ['indexOf'],
|
|
'wrap': [],
|
|
'zip': ['max', 'pluck']
|
|
};
|
|
|
|
/** Used to indicate core functions */
|
|
var coreFuncs = ['extend', 'forEach', 'mixin'];
|
|
|
|
/** Used to determine the remaining functions in the source */
|
|
var funcNames = Object.keys(dependencyMap);
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Gets the aliases associated with a given `funcName`.
|
|
*
|
|
* @private
|
|
* @param {String} funcName The name of the function to get aliases for.
|
|
* @returns {Array} Returns an array of aliases.
|
|
*/
|
|
function getAliases(funcName) {
|
|
return realToAliasMap[funcName] || [];
|
|
}
|
|
|
|
/**
|
|
* Gets an array of depenants for a function by the given `funcName`.
|
|
*
|
|
* @private
|
|
* @param {String} funcName The name of the function to query.
|
|
* @returns {Array} Returns an array of function dependants.
|
|
*/
|
|
function getDependants(funcName) {
|
|
// iterate over `dependencyMap`, adding the names of functions that
|
|
// have `funcName` as a dependency
|
|
return lodash.reduce(dependencyMap, function(result, dependencies, otherName) {
|
|
if (dependencies.indexOf(funcName) > -1) {
|
|
result.push(otherName);
|
|
}
|
|
return result;
|
|
}, []);
|
|
}
|
|
|
|
/**
|
|
* Gets an array of dependencies for a function of the given `funcName`.
|
|
*
|
|
* @private
|
|
* @param {String} funcName The name of the function to query.
|
|
* @returns {Array} Returns an array of function dependencies.
|
|
*/
|
|
function getDependencies(funcName) {
|
|
var dependencies = dependencyMap[funcName],
|
|
result = [];
|
|
|
|
if (!dependencies) {
|
|
return result;
|
|
}
|
|
// recursively accumulate the dependencies of the `funcName` function, and
|
|
// the dependencies of its dependencies, and so on.
|
|
return dependencies.reduce(function(result, otherName) {
|
|
result.push.apply(result, getDependencies(otherName).concat(otherName));
|
|
return result;
|
|
}, result);
|
|
}
|
|
|
|
/**
|
|
* Gets the real name, not alias, of a given `funcName`.
|
|
*
|
|
* @private
|
|
* @param {String} funcName The name of the function to resolve.
|
|
* @returns {String} Returns the real name.
|
|
*/
|
|
function getRealName(funcName) {
|
|
return aliasToRealMap[funcName] || funcName;
|
|
}
|
|
|
|
/**
|
|
* Determines if all functions of the given names have been removed.
|
|
*
|
|
* @private
|
|
* @param {String} [funcName1, funcName2, ...] The names of functions to check.
|
|
* @returns {Boolean} Returns `true` if all functions have been removed, else `false`.
|
|
*/
|
|
function isRemoved() {
|
|
return !lodash.intersection(funcNames, arguments).length;
|
|
}
|
|
|
|
/**
|
|
* Removes the `funcName` function declaration, expression, or assignment and
|
|
* associated code from the `source`.
|
|
*
|
|
* @private
|
|
* @param {String} source The source to process.
|
|
* @param {String} funcName The name of the function to remove.
|
|
* @returns {String} Returns the source with the function removed.
|
|
*/
|
|
function removeFunction(source, funcName) {
|
|
// remove function
|
|
source = source.replace(RegExp(
|
|
// match multi-line comment block (could be on a single line)
|
|
'\\n +/\\*[^*]*\\*+(?:[^/][^*]*\\*+)*/\\n' +
|
|
// begin non-capturing group
|
|
'(?:' +
|
|
// match a function declaration
|
|
'( +)function ' + funcName + '\\b[\\s\\S]+?\\n\\1}|' +
|
|
// match a variable declaration with `createIerator`
|
|
' +var ' + funcName + ' *= *(?:[a-zA-Z]+ *\\|\\| *)?createIterator\\((?:{|[a-zA-Z])[\\s\\S]+?\\);|' +
|
|
// match a variable declaration with function expression
|
|
'( +)var ' + funcName + ' *= *(?:[a-zA-Z]+ *\\|\\| *)?function[\\s\\S]+?\\n\\2};' +
|
|
// end non-capturing group
|
|
')\\n'
|
|
), '');
|
|
|
|
// exit early if function is already removed
|
|
var found = funcNames.indexOf(funcName);
|
|
if (found < 0) {
|
|
return source;
|
|
}
|
|
|
|
// grab `lodash` method assignments snippet
|
|
var assignmentSnippet = source.match(/( +)extend\(lodash,(?:[\s\S]+?\},)?([\s\S]+?\n\1}\))/)[2];
|
|
|
|
// remove `funcName` from method assignments
|
|
var modifiedSnippet = getAliases(funcName).concat(funcName).reduce(function(result, otherName) {
|
|
result = result.replace(RegExp(" *'" + otherName + "'[^\\n]+\\n"), '');
|
|
return result;
|
|
}, assignmentSnippet)
|
|
|
|
// remove any trailing commas and comments from the method assignments
|
|
modifiedSnippet = modifiedSnippet.replace(/,(?:\s*\/\/[^\n]*)?(\s*}\))/, '$1');
|
|
|
|
// replace method assignments snippet with the modified snippet
|
|
source = source.replace(assignmentSnippet, modifiedSnippet);
|
|
|
|
// remove from remaining function names
|
|
funcNames.splice(found, 1);
|
|
|
|
// remove associated code snippets
|
|
switch(funcName) {
|
|
case 'isArguments':
|
|
// remove `isArguments` if-statement
|
|
source = source.replace(/ +(?:\/\/[^\n]*\s+)?if *\(!isArguments[^)]+\)[\s\S]+?};?\s*}\n/, '');
|
|
break;
|
|
|
|
case 'template':
|
|
// remove associated functions
|
|
['detokenize', 'escapeChar', 'tokenizeEscape', 'tokenizeInterpolate', 'tokenizeEvaluate'].forEach(function(otherName) {
|
|
source = removeFunction(source, otherName);
|
|
});
|
|
// remove associated variables
|
|
['escapes', 'iteratorTemplate', 'reEscapeDelimiter', 'reEvaluateDelimiter', 'reInterpolateDelimiter', 'reToken', 'reUnescaped', 'token', 'tokenized'].forEach(function(varName) {
|
|
source = removeVar(source, varName);
|
|
});
|
|
// remove `templateSettings` assignment
|
|
source = source.replace(/\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/\n( +)'templateSettings'[\s\S]+?},\n/, '');
|
|
break;
|
|
|
|
case 'uniqueId':
|
|
source = removeVar(source, 'idCounter');
|
|
}
|
|
return source;
|
|
}
|
|
|
|
/**
|
|
* Removes a given variable from the `source`.
|
|
*
|
|
* @private
|
|
* @param {String} source The source to process.
|
|
* @param {String} varName The name of the variable to remove.
|
|
* @returns {String} Returns the source with the variable removed.
|
|
*/
|
|
function removeVar(source, varName) {
|
|
return source.replace(RegExp(
|
|
// match multi-line comment block
|
|
'\\n +/\\*[^*]*\\*+(?:[^/][^*]*\\*+)*/\\n' +
|
|
// match a variable declaration
|
|
'( +)var ' + varName + ' *= *(?:.*?;|[\\s\\S]+?\\n\\1[^\\n]+;)\\n'
|
|
), '');
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
// custom build
|
|
process.argv.some(function(arg) {
|
|
// exit early if not the "exclude" or "include" command option
|
|
var pair = arg.match(/^(exclude|include)=(.+)$/);
|
|
if (!pair) {
|
|
return false;
|
|
}
|
|
|
|
var filterType = pair[1],
|
|
filterNames = pair[2].split(',');
|
|
|
|
// set custom build flag
|
|
isCustom = true;
|
|
|
|
// remove the specified functions and their dependants
|
|
if (filterType == 'exclude') {
|
|
filterNames.forEach(function(funcName) {
|
|
funcName = getRealName(funcName);
|
|
var otherNames = getDependants(funcName).concat(funcName);
|
|
|
|
// skip removal if `funcName` is a required core function
|
|
if (otherNames.some(function(otherName) {
|
|
return coreFuncs.indexOf(otherName) > -1;
|
|
})) {
|
|
return;
|
|
}
|
|
otherNames.forEach(function(otherName) {
|
|
source = removeFunction(source, otherName);
|
|
});
|
|
});
|
|
}
|
|
// else remove all but the specified functions and their dependencies
|
|
else {
|
|
filterNames = lodash.uniq(filterNames.concat(coreFuncs).reduce(function(result, funcName) {
|
|
funcName = getRealName(funcName);
|
|
result.push.apply(result, getDependencies(funcName).concat(funcName));
|
|
return result;
|
|
}, []));
|
|
|
|
lodash.each(dependencyMap, function(dependencies, otherName) {
|
|
if (filterNames.indexOf(otherName) < 0) {
|
|
source = removeFunction(source, otherName);
|
|
}
|
|
});
|
|
}
|
|
|
|
// remove shared variables
|
|
if (isRemoved('createIterator', 'isEqual')) {
|
|
source = removeVar(source, 'hasDontEnumBug');
|
|
}
|
|
if (isRemoved('every', 'filter', 'find', 'forEach', 'groupBy', 'map', 'reject', 'some')) {
|
|
source = removeVar(source, 'baseIteratorOptions');
|
|
}
|
|
if (isRemoved('every', 'some')) {
|
|
source = removeVar(source, 'everyIteratorOptions');
|
|
}
|
|
if (isRemoved('defaults', 'extend')) {
|
|
source = removeVar(source, 'extendIteratorOptions');
|
|
}
|
|
if (isRemoved('filter', 'reject')) {
|
|
source = removeVar(source, 'filterIteratorOptions');
|
|
}
|
|
if (isRemoved('map', 'pluck', 'values')) {
|
|
source = removeVar(source, 'mapIteratorOptions');
|
|
}
|
|
if (isRemoved('max', 'min')) {
|
|
// remove varaible and associated try-catch
|
|
source = removeVar(source, 'argsLimit');
|
|
source = source.replace(/\n *try\s*\{\s*\(function[\s\S]+?catch[^}]+}\n/, '');
|
|
}
|
|
|
|
// consolidate consecutive horizontal rule comment separators
|
|
source = source.replace(/(?:\s*\/\*-+\*\/\s*){2,}/g, function(separators) {
|
|
return separators.match(/^\s*/)[0] + separators.slice(separators.lastIndexOf('/*'));
|
|
});
|
|
|
|
return true;
|
|
});
|
|
|
|
// begin the minification process
|
|
if (isCustom) {
|
|
minify(source, 'lodash.custom.min', function(result) {
|
|
fs.writeFileSync(path.join(__dirname, 'lodash.custom.js'), source);
|
|
fs.writeFileSync(path.join(__dirname, 'lodash.custom.min.js'), result);
|
|
});
|
|
}
|
|
else {
|
|
minify(source, 'lodash.min', function(result) {
|
|
fs.writeFileSync(path.join(__dirname, 'lodash.min.js'), result);
|
|
});
|
|
}
|
|
}());
|