Add custom build options to build.js.

Former-commit-id: 4df20584d5c8852caf0f123d618dbf61f18c220c
This commit is contained in:
John-David Dalton
2012-05-19 23:54:07 -04:00
parent 1dd9ee2397
commit 6dfe18fe18

254
build.js
View File

@@ -13,6 +13,9 @@
/** Flag used to specify a custom build */
var isCustom = false;
/** Shortcut used to convert array-like objects to arrays */
var slice = [].slice;
/** The lodash.js source */
var source = fs.readFileSync(path.join(__dirname, 'lodash.js'), 'utf8');
@@ -64,7 +67,7 @@
'compact': [],
'compose': [],
'contains': ['createIterator'],
'createIterator': ['template'],
'createIterator': [],
'debounce': [],
'defaults': ['createIterator'],
'defer': [],
@@ -140,12 +143,6 @@
'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);
/*--------------------------------------------------------------------------*/
/**
@@ -211,14 +208,60 @@
}
/**
* Determines if all functions of the given names have been removed.
* Determines if all functions of the given names have been removed from the `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() {
return !lodash.intersection(funcNames, arguments).length;
function isRemoved(source) {
return slice.call(arguments, 1).every(function(funcName) {
return !matchFunction(source, funcName);
});
}
/**
* Searches the `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 (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 `createIterator`
' +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'
));
return result ? result[0] : '';
}
/**
* Removes the all references to `refName` from the `createIterator` source.
*
* @private
* @param {String} source The source to process.
* @param {String} refName The name of the reference to remove.
* @returns {String} Returns the modified source.
*/
function removeFromCreateIterator(source, refName) {
var snippet = matchFunction(source, 'createIterator'),
modified = snippet.replace(RegExp('\\b' + refName + '\\b,? *', 'g'), '');
return source.replace(snippet, modified);
}
/**
@@ -231,70 +274,29 @@
* @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'
), '');
var modified,
snippet = matchFunction(source, funcName);
// exit early if function is already removed
var found = funcNames.indexOf(funcName);
if (found < 0) {
// exit early if function is not found
if (!snippet) {
return source;
}
// grab `lodash` method assignments snippet
var assignmentSnippet = source.match(/( +)extend\(lodash,(?:[\s\S]+?\},)?([\s\S]+?\n\1}\))/)[2];
// remove function
source = source.replace(matchFunction(source, funcName), '');
// 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)
// grab the method assignments snippet
snippet = source.match(/lodash\.VERSION *= *[\s\S]+?\/\*-+\*\/\n/)[0];
// remove any trailing commas and comments from the method assignments
modifiedSnippet = modifiedSnippet.replace(/,(?:\s*\/\/[^\n]*)?(\s*}\))/, '$1');
// remove assignment and aliases
modified = getAliases(funcName).concat(funcName).reduce(function(result, otherName) {
return result.replace(RegExp('(?:\\n *//.*\\s*)* *lodash\\.' + otherName + ' *= *.+\\n'), '');
}, snippet);
// replace method assignments snippet with the modified snippet
source = source.replace(assignmentSnippet, modifiedSnippet);
// replace with the modified snippet
source = source.replace(snippet, modified);
// 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;
return removeFromCreateIterator(source, funcName);
}
/**
@@ -306,16 +308,61 @@
* @returns {String} Returns the source with the variable removed.
*/
function removeVar(source, varName) {
return source.replace(RegExp(
source = source.replace(RegExp(
// begin non-capturing group
'(?:' +
// match multi-line comment block
'\\n +/\\*[^*]*\\*+(?:[^/][^*]*\\*+)*/\\n' +
// match a variable declaration
'( +)var ' + varName + ' *= *(?:.*?;|[\\s\\S]+?\\n\\1[^\\n]+;)\\n'
'(?:\\n +/\\*[^*]*\\*+(?:[^/][^*]*\\*+)*/)?\\n' +
// match a variable declaration that's not part of a declaration list
'( +)var ' + varName + ' *= *(?:.*?;|(?:Function\\(.+?|.*?[^,])\\n[\\s\\S]+?\\n\\1.+?;)\\n|' +
// match a variable in a declaration list
'\\n +' + varName + ' *=.*?,' +
// end non-capturing group
')'
), '');
// 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);
}
/*--------------------------------------------------------------------------*/
// inline `iteratorTemplate`
(function() {
var iteratorTemplate = lodash._iteratorTemplate,
code = /^function[^{]+{([\s\S]+?)}$/.exec(iteratorTemplate)[1];
// remove whitespace from template
code = code.replace(/\[object |else if|function | in |return\s+[\w']|throw |typeof |var |\\\\n|\\n|\s+/g, function(match) {
return match == false || match == '\\n' ? '' : match;
});
// remove unnecessary code
code = code
.replace(/\|\|\{\}|,__t,__j=Array.prototype.join|function print[^}]+}|\+''/g, '')
.replace(/(\{);|;(\})/g, '$1$2')
.replace(/\(\(__t=\(([^)]+)\)\)==null\?'':__t\)/g, '$1');
// ensure escaped characters are interpreted correctly inside the `Function()` string
code = code.replace(/\\/g, '\\\\');
// add `code` to `Function()`
code = '$1Function(\'object\',\n$2 "' + code + '"\n$2);\n';
// replace `template()` with `Function()`
source = source.replace(/(( +)var iteratorTemplate *= *)([\s\S]+?\n\2.+?);\n/, code);
// remove pseudo private property `_iteratorTemplate`
source = source.replace(/(?:\s*\/\/.*)*\s*lodash\._iteratorTemplate\b.+\n/, '\n');
}());
/*--------------------------------------------------------------------------*/
// custom build
process.argv.some(function(arg) {
// exit early if not the "exclude" or "include" command option
@@ -325,7 +372,7 @@
}
var filterType = pair[1],
filterNames = pair[2].split(',');
filterNames = lodash.intersection(Object.keys(dependencyMap), pair[2].split(',').map(getRealName));
// set custom build flag
isCustom = true;
@@ -333,24 +380,14 @@
// 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) {
getDependants(funcName).concat(funcName).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);
filterNames = lodash.uniq(filterNames.reduce(function(result, funcName) {
result.push.apply(result, getDependencies(funcName).concat(funcName));
return result;
}, []));
@@ -362,29 +399,40 @@
});
}
// remove shared variables
if (isRemoved('createIterator', 'isEqual')) {
source = removeVar(source, 'hasDontEnumBug');
// remove associated functions, variables and code snippets
if (isRemoved('isArguments')) {
// remove `isArguments` if-statement
source = source.replace(/(?:\s*\/\/.*)*\s*if *\(!isArguments[^)]+\)[\s\S]+?};?\s*}\n/, '');
}
if (isRemoved('every', 'filter', 'find', 'forEach', 'groupBy', 'map', 'reject', 'some')) {
source = removeVar(source, 'baseIteratorOptions');
if (isRemoved('mixin')) {
// remove `LoDash` constructor
source = removeFunction(source, 'LoDash');
// remove `LoDash` calls
source = source.replace(/(?:new +LoDash(?!\()|(?:new +)?LoDash\([^)]*\));?/g, '');
// remove `LoDash.prototype` additions
source = source.replace(/(?:\s*\/\/.*)*\s*LoDash.prototype *=[\s\S]+?\/\*-+\*\//, '');
}
if (isRemoved('every', 'some')) {
source = removeVar(source, 'everyIteratorOptions');
if (isRemoved(source, 'template')) {
// remove `templateSettings` assignment
source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *lodash\.templateSettings[\s\S]+?};\n/, '');
}
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
if (isRemoved(source, 'max', 'min')) {
source = removeVar(source, 'argsLimit');
source = source.replace(/\n *try\s*\{\s*\(function[\s\S]+?catch[^}]+}\n/, '');
// remove `argsLimit` try-catch
source = source.replace(/\n *try\s*\{[\s\S]+?argsLimit *=[\s\S]+?catch[^}]+}\n/, '');
}
if (isRemoved(source, 'isArray', 'isEmpty', 'isEqual', 'size')) {
source = removeVar(source, 'arrayClass');
}
if (isRemoved(source, 'bind', 'functions', 'groupBy', 'invoke', 'isEqual', 'isFunction', 'result', 'sortBy', 'toArray')) {
source = removeVar(source, 'funcClass');
}
if (isRemoved(source, 'clone', 'isObject', 'keys')) {
source = removeVar(source, 'objectTypes');
source = removeFromCreateIterator(source, 'objectTypes');
}
if (isRemoved(source, 'isEmpty', 'isEqual', 'isString', 'size')) {
source = removeVar(source, 'stringClass');
}
// consolidate consecutive horizontal rule comment separators
@@ -397,8 +445,8 @@
// begin the minification process
if (isCustom) {
fs.writeFileSync(path.join(__dirname, 'lodash.custom.js'), source);
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);
});
}