Permalink
Browse files

Add custom build options to build.js.

  • Loading branch information...
1 parent 4592835 commit be6f64b7de6da53fafb3c87cf82c820de723d303 @jdalton jdalton committed May 20, 2012
Showing with 143 additions and 95 deletions.
  1. +143 −95 build.js
View
238 build.js
@@ -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,90 +208,95 @@
}
/**
- * 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);
+ });
}
/**
- * Removes the `funcName` function declaration, expression, or assignment and
- * associated code from the `source`.
+ * Searches the `source` for a `funcName` function declaration, expression, or
+ * assignment and returns the matched snippet.
*
* @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.
+ * @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 removeFunction(source, funcName) {
- // remove function
- source = source.replace(RegExp(
+ 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 `createIerator`
+ // 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'
- ), '');
+ ));
- // exit early if function is already removed
- var found = funcNames.indexOf(funcName);
- if (found < 0) {
- return source;
- }
+ return result ? result[0] : '';
+ }
- // grab `lodash` method assignments snippet
- var assignmentSnippet = source.match(/( +)extend\(lodash,(?:[\s\S]+?\},)?([\s\S]+?\n\1}\))/)[2];
+ /**
+ * 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'), '');
- // 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)
+ return source.replace(snippet, modified);
+ }
- // remove any trailing commas and comments from the method assignments
- modifiedSnippet = modifiedSnippet.replace(/,(?:\s*\/\/[^\n]*)?(\s*}\))/, '$1');
+ /**
+ * 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) {
+ var modified,
+ snippet = matchFunction(source, funcName);
- // replace method assignments snippet with the modified snippet
- source = source.replace(assignmentSnippet, modifiedSnippet);
+ // exit early if function is not found
+ if (!snippet) {
+ return source;
+ }
- // remove from remaining function names
- funcNames.splice(found, 1);
+ // remove function
+ source = source.replace(matchFunction(source, funcName), '');
- // remove associated code snippets
- switch(funcName) {
- case 'isArguments':
- // remove `isArguments` if-statement
- source = source.replace(/ +(?:\/\/[^\n]*\s+)?if *\(!isArguments[^)]+\)[\s\S]+?};?\s*}\n/, '');
- break;
+ // grab the method assignments snippet
+ snippet = source.match(/lodash\.VERSION *= *[\s\S]+?\/\*-+\*\/\n/)[0];
- 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;
+ // remove assignment and aliases
+ modified = getAliases(funcName).concat(funcName).reduce(function(result, otherName) {
+ return result.replace(RegExp('(?:\\n *//.*\\s*)* *lodash\\.' + otherName + ' *= *.+\\n'), '');
+ }, snippet);
- case 'uniqueId':
- source = removeVar(source, 'idCounter');
- }
- return source;
+ // replace with the modified snippet
+ source = source.replace(snippet, modified);
+
+ 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,32 +372,22 @@
}
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;
// 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(source, 'max', 'min')) {
+ source = removeVar(source, 'argsLimit');
+ // remove `argsLimit` try-catch
+ source = source.replace(/\n *try\s*\{[\s\S]+?argsLimit *=[\s\S]+?catch[^}]+}\n/, '');
}
- if (isRemoved('filter', 'reject')) {
- source = removeVar(source, 'filterIteratorOptions');
+ if (isRemoved(source, 'isArray', 'isEmpty', 'isEqual', 'size')) {
+ source = removeVar(source, 'arrayClass');
}
- if (isRemoved('map', 'pluck', 'values')) {
- source = removeVar(source, 'mapIteratorOptions');
+ if (isRemoved(source, 'bind', 'functions', 'groupBy', 'invoke', 'isEqual', 'isFunction', 'result', 'sortBy', 'toArray')) {
+ source = removeVar(source, 'funcClass');
}
- 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/, '');
+ 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);
});
}

0 comments on commit be6f64b

Please sign in to comment.