From d49318582f13e1546d5de148c09649ef699d5cab Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Sun, 8 Jul 2012 03:28:18 -0400 Subject: [PATCH] Adjust how "mobile" build is created and add first pass at "legacy" build. Former-commit-id: 740cc40c21d33353f34796ae6da3bc8ce015ab6c --- build.js | 126 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 97 insertions(+), 29 deletions(-) diff --git a/build.js b/build.js index 438fec965..d5078485c 100755 --- a/build.js +++ b/build.js @@ -2,20 +2,43 @@ ;(function() { 'use strict'; - /** The Node filesystem and path modules */ + /** Load modules */ var fs = require('fs'), - path = require('path'); - - /** Load other modules */ - var lodash = require(path.join(__dirname, 'lodash')), + path = require('path'), + vm = require('vm'), minify = require(path.join(__dirname, 'build', 'minify')); + /** Flag used to specify a backbone build */ + var isBackbone = process.argv.indexOf('backbone') > -1; + + /** Flag used to specify a legacy build */ + var isLegacy = process.argv.indexOf('legacy') > -1; + + /** Flag used to specify a mobile build */ + var isMobile = !isLegacy && process.argv.indexOf('mobile') > -1; + /** 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'); + /** Load customized Lo-Dash module */ + var lodash = (function() { + var sandbox = {}; + + if (isLegacy) { + ['isBindFast', 'isKeysFast', 'nativeBind', 'nativeIsArray', 'nativeKeys'].forEach(function(varName) { + source = replaceVar(source, varName, 'false'); + }); + } + else if (isMobile) { + source = replaceVar(source, 'isKeysFast', 'false'); + } + vm.runInNewContext(source, sandbox); + return sandbox._; + }()); + /** Used to associate aliases with their real names */ var aliasToRealMap = { 'all': 'every', @@ -198,6 +221,7 @@ 'hasDontEnumBug', 'inLoop', 'init', + 'isKeysFast', 'iteratedObject', 'loopExp', 'object', @@ -233,12 +257,6 @@ return pair[1]; }, ''); - /** Flag used to specify a backbone build */ - var isBackbone = process.argv.indexOf('backbone') > -1; - - /** Flag used to specify a mobile build */ - var isMobile = process.argv.indexOf('mobile') > -1; - /*--------------------------------------------------------------------------*/ /** @@ -318,7 +336,7 @@ * @returns {String} Returns the `isArguments` fallback snippet. */ function getIsArgumentsFallback(source) { - return (source.match(/(?: *\/\/.*)*\s*if *\(!(?:lodash\.)?isArguments[^)]+\)[\s\S]+?};?\s*}\n/) || [''])[0]; + return (source.match(/(?: *\/\/.*)*\s*if *\(!(?:lodash\.)?isArguments[^)]+\)[\s\S]+?};\s*}\n/) || [''])[0]; } /** @@ -465,6 +483,31 @@ return removeFromCreateIterator(source, varName); } + /** + * Searches `source` for a `varName` variable declaration and replaces its + * assigned value with `varValue`. + * + * @private + * @param {String} source The source to inspect. + * @param {String} varName The name of the variable to replace. + * @returns {String} Returns the source with the variable replaced. + */ + function replaceVar(source, varName, varValue) { + // replace a variable that's not part of a declaration list + source = source.replace(RegExp( + '(( +)var ' + varName + ' *= *)' + + '(?:.*?;|(?:Function\\(.+?|.*?[^,])\\n[\\s\\S]+?\\n\\2.+?;)\\n' + ), '$1' + varValue + ';\n'); + + // replace a varaible at the start of middle of a declaration list + source = source.replace(RegExp('((?:var|\\n) +' + varName + ' *=).+?,'), '$1 ' + varValue + ','); + + // replace a variable at the end of a variable declaration list + source = source.replace(RegExp('(,\\s*' + varName + ' *=).*?;'), '$1 ' + varValue + ';'); + + return source; + } + /*--------------------------------------------------------------------------*/ // Backbone build @@ -574,13 +617,12 @@ } if (isRemoved(source, 'bind')) { source = removeVar(source, 'nativeBind'); - source = removeVar(source, 'useNativeBind'); + source = removeVar(source, 'isBindFast'); } if (isRemoved(source, 'isArray')) { source = removeVar(source, 'nativeIsArray'); } if (isRemoved(source, 'keys')) { - source = removeVar(source, 'nativeKeys'); source = removeFunction(source, 'shimKeys'); } if (isRemoved(source, 'clone', 'isObject', 'keys')) { @@ -637,6 +679,10 @@ 'RegExp': 'regexpClass', 'String': 'stringClass' }, function(value, key) { + // if legacy build skip `isArguments` + if (isLegacy && key == 'Arguments') { + return; + } var funcName = 'is' + key, funcCode = matchFunction(source, funcName); @@ -673,7 +719,7 @@ ); // tweak `isArguments` fallback - snippet = getIsArgumentsFallback(source); + snippet = !isLegacy && getIsArgumentsFallback(source); if (snippet) { result = '\n' + snippet.replace(/isArguments/g, 'lodash.$&'); source = source.replace(snippet, result); @@ -682,6 +728,29 @@ /*--------------------------------------------------------------------------*/ + if (isLegacy) { + // 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]+?var shimKeys *=/, '') + ); + + source = removeFunction(source, 'shimKeys'); + } + // replace `_.isArguments` with fallback + if (!isRemoved(source, 'isArguments')) { + source = source.replace( + matchFunction(source, 'isArguments').replace(/[\s\S]+?var isArguments *=/, ''), + getIsArgumentsFallback(source).match(/isArguments *=([\s\S]+?) *};/)[1] + ' };\n' + ); + + source = removeIsArgumentsFallback(source); + } + + source = removeFromCreateIterator(source, 'nativeKeys'); + } + if (isMobile) { // inline functions defined with `createIterator` lodash.functions(lodash).forEach(function(funcName) { @@ -696,31 +765,27 @@ source = source.replace(reFunc, '$1' + getFunctionSource(lodash[funcName]) + ';\n'); }); - source = removeIsArgumentsFallback(source); - - source = removeVar(source, 'iteratorTemplate'); - // remove JScript [[DontEnum]] fix from `isEqual` source = source.replace(/(?:\s*\/\/.*\n)*( +)if *\(result *&& *hasDontEnumBug[\s\S]+?\n\1}/, ''); // remove IE `shift` and `splice` fix source = source.replace(/(?:\s*\/\/.*\n)*( +)if *\(value.length *=== *0[\s\S]+?\n\1}/, ''); - // cleanup code - source = source.replace(/^ *;\n/gm, ''); + source = removeVar(source, 'iteratorTemplate'); + source = removeIsArgumentsFallback(source); } else { // inline `iteratorTemplate` template source = source.replace(/(( +)var iteratorTemplate *= *)[\s\S]+?\n\2.+?;\n/, (function() { - var code = getFunctionSource(lodash._iteratorTemplate); + var snippet = getFunctionSource(lodash._iteratorTemplate); // expand properties to avoid having to use a with-statement iteratorOptions.forEach(function(property) { - code = code.replace(RegExp('([^\\w.])\\b' + property + '\\b', 'g'), '$1obj.' + property); + snippet = snippet.replace(RegExp('([^\\w.])\\b' + property + '\\b', 'g'), '$1obj.' + property); }); // remove unnecessary code - code = code + snippet = snippet .replace(/, *__t,[^;]+|function print[^}]+}/g, '') .replace(/'(?:\\n|\s)+'/g, "''") .replace(/__p *\+= *' *';/g, '') @@ -729,17 +794,17 @@ .replace(/\(\(__w?t *= *\( *([^)]+) *\)\) *== *null *\? *'' *: *__w?t\)/g, '$1'); // remove the with-statement - code = code.replace(/ *with *\([^)]+\) *{/, '\n').replace(/}}\s*;?\s*}/, '}}\n'); + snippet = snippet.replace(/ *with *\([^)]+\) *{/, '\n').replace(/}}\s*;?\s*}/, '}}\n'); // minor cleanup - code = code + snippet = snippet .replace(/obj *\|\| *\(obj *= *\{}\);/, '') .replace(/var __p;\s*__p/, 'var __p'); // remove comments, including sourceURLs - code = code.replace(/\s*\/\/.*(?:\n|$)/g, ''); + snippet = snippet.replace(/\s*\/\/.*(?:\n|$)/g, ''); - return '$1' + code + ';\n'; + return '$1' + snippet + ';\n'; }())); } @@ -748,8 +813,11 @@ // remove pseudo private properties source = source.replace(/(?:(?:\s*\/\/.*)*\s*lodash\._[^=]+=.+\n)+/g, '\n'); + // cleanup code + source = source.replace(/^ *;\n/gm, ''); + // begin the minification process - if (filterType || isBackbone || isMobile) { + if (filterType || isBackbone || isLegacy || isMobile) { fs.writeFileSync(path.join(__dirname, 'lodash.custom.js'), source); minify(source, 'lodash.custom.min', function(result) { fs.writeFileSync(path.join(__dirname, 'lodash.custom.min.js'), result);