From cf5e5dbe554616aa4fa1c18cbedca789c8c0b8f0 Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Sat, 8 Dec 2012 18:46:09 -0800 Subject: [PATCH] Update build to add `_.chain` and `_#chain` for `backbone` and `underscore` builds. Former-commit-id: 2c910de419904a1285d246a3b08d87cb4daafa18 --- build.js | 171 +++++++++++++++++++++++++++++-------------- build/pre-compile.js | 4 +- test/test-build.js | 74 +++++++++++++++---- 3 files changed, 176 insertions(+), 73 deletions(-) diff --git a/build.js b/build.js index c47c5cae1..e8476b684 100755 --- a/build.js +++ b/build.js @@ -70,7 +70,6 @@ 'bind': ['isFunction', 'isObject'], 'bindAll': ['bind', 'functions'], 'bindKey': ['isFunction', 'isObject'], - 'chain': ['mixin'], 'clone': ['assign', 'forEach', 'forOwn', 'isArray', 'isObject'], 'compact': [], 'compose': [], @@ -159,7 +158,10 @@ 'where': ['filter', 'keys'], 'without': ['indexOf'], 'wrap': [], - 'zip': ['max', 'pluck'] + 'zip': ['max', 'pluck'], + + // method used by the `backbone` and `underscore` builds + 'chain': ['mixin'] }; /** Used to inline `iteratorTemplate` */ @@ -1175,7 +1177,117 @@ source = replaceVar(source, 'noArgsClass', 'true'); source = removeKeysOptimization(source); } - else if (isUnderscore) { + // add Underscore's chaining API + if (isBackbone || isUnderscore) { + // add `_.chain` + source = source.replace(matchFunction(source, 'tap'), function(match) { + return [ + '', + ' /**', + ' * 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 lodash(value);', + ' value.__chain__ = true;', + ' return value;', + ' }', + '', + match + ].join('\n'); + }); + + // add `wrapperChain` + source = source.replace(matchFunction(source, 'wrapperToString'), function(match) { + return [ + '', + ' /**', + ' * 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'); + }); + + // add `__chain__` checks to `_.mixin` and `Array` function wrappers + _.each([ + matchFunction(source, 'mixin'), + /(?:\s*\/\/.*)*\n( *)forEach\(\['[\s\S]+?\n\1}.+/g + ], function(pattern) { + source = source.replace(pattern, function(match) { + return match.replace(/( *)return new lodash\(([^)]+)\).+/, function(submatch, indent, varName) { + return indent + [ + 'if (this.__chain__) {', + ' varName = new lodash(varName);', + ' varName.__chain__ = true;', + '}', + 'return varName;' + ].join('\n' + indent) + .replace(/varName/g, varName); + }); + }); + }); + + // 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*\/\/.*)*\s*mixin\(lodash\).+/, ''); + source = source.replace(getMethodAssignments(source), function(match) { + return match + [ + '', + '', + ' // add functions to `lodash.prototype`', + ' mixin(lodash);' + ].join('\n'); + }); + } + if (isUnderscore) { // remove unneeded variables source = removeVar(source, 'cloneableClasses'); source = removeVar(source, 'ctorByClass'); @@ -1202,15 +1314,6 @@ ' }' ].join('\n')); - // replace `_.chain` - source = replaceFunction(source, 'chain', [ - ' function chain(value) {', - ' value = new lodash(value);', - ' value.__chain__ = true;', - ' return value;', - ' }' - ].join('\n')); - // replace `_.clone` if (useUnderscoreClone) { source = replaceFunction(source, 'clone', [ @@ -1460,33 +1563,6 @@ ' }' ].join('\n')); - // replace `wrapperChain` - source = replaceFunction(source, 'wrapperChain', [ - ' function wrapperChain() {', - ' this.__chain__ = true;', - ' return this;', - ' }' - ].join('\n')); - - // add `__chain__` checks to `_.mixin` and `Array` function wrappers - _.each([ - matchFunction(source, 'mixin'), - /(?:\s*\/\/.*)*\n( *)forEach\(\['[\s\S]+?\n\1}.+/g - ], function(pattern) { - source = source.replace(pattern, function(match) { - return match.replace(/( *)return new lodash\(([^)]+)\).+/, function(submatch, indent, varName) { - return indent + [ - 'if (this.__chain__) {', - ' varName = new lodash(varName);', - ' varName.__chain__ = true;', - '}', - 'return varName;' - ].join('\n' + indent) - .replace(/varName/g, varName); - }); - }); - }); - // remove `arguments` object check from `_.isEqual` source = source.replace(matchFunction(source, 'isEqual'), function(match) { return match.replace(/^ *if *\(.+== argsClass[^}]+}\n/gm, '') @@ -1499,23 +1575,6 @@ }); }); - // 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*\/\/.*)*\s*mixin\(lodash\).+/, ''); - source = source.replace(getMethodAssignments(source), function(match) { - return match + [ - '', - '', - ' // add functions to `lodash.prototype`', - ' mixin(lodash);' - ].join('\n'); - }); - // remove unused features from `createBound` if (buildMethods.indexOf('partial') == -1) { source = source.replace(matchFunction(source, 'createBound'), function(match) { diff --git a/build/pre-compile.js b/build/pre-compile.js index c877082cb..e02f303e2 100644 --- a/build/pre-compile.js +++ b/build/pre-compile.js @@ -68,7 +68,6 @@ 'bind', 'bindAll', 'bindKey', - 'chain', 'clone', 'collect', 'compact', @@ -188,8 +187,9 @@ 'wrap', 'zip', - // property used by the `lodash underscore` build + // properties used by the `backbone` and `underscore` builds '__chain__', + 'chain', // properties used by underscore.js '_chain', diff --git a/test/test-build.js b/test/test-build.js index 6dfed942d..e2d7d8e9c 100644 --- a/test/test-build.js +++ b/test/test-build.js @@ -54,9 +54,9 @@ }; /** List of all Lo-Dash methods */ - var allMethods = _.functions(_).filter(function(methodName) { - return !/^_/.test(methodName); - }); + var allMethods = _.functions(_) + .filter(function(methodName) { return !/^_/.test(methodName); }) + .concat('chain'); /** List of "Arrays" category methods */ var arraysMethods = [ @@ -86,7 +86,6 @@ /** List of "Chaining" category methods */ var chainingMethods = [ - 'chain', 'mixin', 'tap', 'value' @@ -498,7 +497,12 @@ }); }); - ['', 'moduleId=underscore'].forEach(function(command) { + var commands = [ + '', + 'moduleId=underscore' + ]; + + commands.forEach(function(command) { asyncTest('`lodash template=*.jst` exports=amd' + (command ? ' ' + command : ''), function() { var start = _.after(2, _.once(QUnit.start)); @@ -637,6 +641,44 @@ /*--------------------------------------------------------------------------*/ + QUnit.module('underscore chaining methods'); + + (function() { + var commands = [ + 'backbone', + 'underscore' + ]; + + commands.forEach(function(command) { + asyncTest('`lodash ' + command +'`', function() { + var start = _.after(2, _.once(QUnit.start)); + + build(['-s', command], function(source, filePath) { + var basename = path.basename(filePath, '.js'), + context = createContext(); + + vm.runInContext(source, context); + var lodash = context._; + + ok(lodash.chain(1) instanceof lodash, '_.chain: ' + basename); + ok(lodash(1).chain() instanceof lodash, '_#chain: ' + basename); + + var wrapped = lodash(1); + equal(wrapped.clone() instanceof lodash, false, '_(...) wrapped values are not chainable by default: ' + basename); + equal(String(wrapped) === '1', false, '_#toString should not be implemented: ' + basename); + equal(Number(wrapped) === 1 , false, '_#valueOf should not be implemented: ' + basename); + + wrapped.chain(); + equal(wrapped.has('x') instanceof lodash, true, '_#has returns wrapped values when chaining: ' + basename); + + start(); + }); + }); + }); + }()); + + /*--------------------------------------------------------------------------*/ + QUnit.module('underscore modifier'); (function() { @@ -701,14 +743,6 @@ equal(lodash.template('${a}', object), '${a}', '_.template should ignore ES6 delimiters: ' + basename); equal(lodash.uniqueId(0), '1', '_.uniqueId should ignore a prefix of `0`: ' + basename); - var wrapped = lodash(1); - equal(wrapped.clone() instanceof lodash, false, '_(...) wrapped values are not chainable by default: ' + basename); - equal(String(wrapped) === '1', false, '_#toString should not be implemented: ' + basename); - equal(Number(wrapped) === 1 , false, '_#valueOf should not be implemented: ' + basename); - - wrapped.chain(); - equal(wrapped.has('x') instanceof lodash, true, '_#has returns wrapped values when chaining: ' + basename); - start(); }); }); @@ -877,7 +911,12 @@ QUnit.module('output options'); (function() { - ['-o a.js', '--output a.js'].forEach(function(command, index) { + var commands = [ + '-o a.js', + '--output a.js' + ]; + + commands.forEach(function(command, index) { asyncTest('`lodash ' + command +'`', function() { var start = _.once(QUnit.start); @@ -894,7 +933,12 @@ QUnit.module('stdout options'); (function() { - ['-c', '--stdout'].forEach(function(command, index) { + var commands = [ + '-c', + '--stdout' + ]; + + commands.forEach(function(command, index) { asyncTest('`lodash ' + command +'`', function() { var written, start = _.once(QUnit.start),