Initial lodash template=… build support.

Former-commit-id: 9d13021463380556a997cb53f5ae89eb22a7b98b
This commit is contained in:
John-David Dalton
2012-09-23 21:28:47 -07:00
parent 8492c13e72
commit 5d2d86bffc
6 changed files with 440 additions and 341 deletions

216
build.js
View File

@@ -67,7 +67,7 @@
'bind': ['isFunction'], 'bind': ['isFunction'],
'bindAll': ['bind', 'isFunction'], 'bindAll': ['bind', 'isFunction'],
'chain': ['mixin'], 'chain': ['mixin'],
'clone': ['extend', 'forIn', 'forOwn', 'isArguments', 'isFunction'], 'clone': ['extend', 'forOwn', 'isArguments', 'isPlainObject'],
'compact': [], 'compact': [],
'compose': [], 'compose': [],
'contains': [], 'contains': [],
@@ -109,6 +109,7 @@
'isNull': [], 'isNull': [],
'isNumber': [], 'isNumber': [],
'isObject': [], 'isObject': [],
'isPlainObject': ['forIn', 'isArguments', 'isFunction'],
'isRegExp': [], 'isRegExp': [],
'isString': [], 'isString': [],
'isUndefined': [], 'isUndefined': [],
@@ -119,7 +120,7 @@
'map': ['identity'], 'map': ['identity'],
'max': [], 'max': [],
'memoize': [], 'memoize': [],
'merge': ['isArguments', 'isArray', 'forIn'], 'merge': ['isArray', 'isPlainObject'],
'min': [], 'min': [],
'mixin': ['forEach', 'functions'], 'mixin': ['forEach', 'functions'],
'noConflict': [], 'noConflict': [],
@@ -236,6 +237,7 @@
var underscoreMethods = _.without.apply(_, [allMethods].concat([ var underscoreMethods = _.without.apply(_, [allMethods].concat([
'forIn', 'forIn',
'forOwn', 'forOwn',
'isPlainObject',
'lateBind', 'lateBind',
'merge', 'merge',
'partial' 'partial'
@@ -251,6 +253,42 @@
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
/**
* Creates a debug and minified build, executing the `callback` for each.
* The `callback` is invoked with 2 arguments; (filepath, source)
*
* @param {Array} options The options array.
* @param {Function} callback The function called per build.
*/
function buildTemplate(templatePattern, templateSettings) {
var directory = path.dirname(templatePattern);
var pattern = RegExp(
path.basename(templatePattern)
.replace(/[.+?^=!:${}()|[\]\/\\]/g, '\\$&')
.replace(/\*/g, '.*?') + '$'
);
var source = [
';(function() {',
' var templates = _.templates || (_.templates = {});'
];
fs.readdirSync(directory).forEach(function(filename) {
var filepath = path.join(directory, filename);
if (pattern.test(filename)) {
var text = fs.readFileSync(filepath, 'utf8'),
precompiled = getFunctionSource(_.template(text, null, templateSettings)),
prop = filename.replace(/\..*$/, '');
source.push(' templates["' + prop + '"] = ' + precompiled + ';');
}
});
source.push('}());');
return source.join('\n');
}
/** /**
* Removes unnecessary comments, whitespace, and pseudo private properties. * Removes unnecessary comments, whitespace, and pseudo private properties.
* *
@@ -294,9 +332,10 @@
' lodash exports=... Comma separated names of ways to export the `LoDash` function', ' lodash exports=... Comma separated names of ways to export the `LoDash` function',
' (i.e. “amd”, “commonjs”, “global”, “node”, and “none”)', ' (i.e. “amd”, “commonjs”, “global”, “node”, and “none”)',
' lodash iife=... Code to replace the immediately-invoked function expression that wraps Lo-Dash', ' lodash iife=... Code to replace the immediately-invoked function expression that wraps Lo-Dash',
' (e.g. !function(window,undefined){%output%}(this))', ' (e.g. `lodash iife="!function(window,undefined){%output%}(this)"`)',
'',
' lodash template=... The file path pattern used for matching template files to compile', ' lodash template=... The file path pattern used for matching template files to compile',
' (e.g. `lodash template=path/to/templates/*.tmpl`)', ' (e.g. `lodash template=./*.jst`)',
'', '',
' All arguments, except `legacy` with `csp` or `mobile`, may be combined.', ' All arguments, except `legacy` with `csp` or `mobile`, may be combined.',
' Unless specified by `-o` or `--output`, all files created are saved to the current working directory.', ' Unless specified by `-o` or `--output`, all files created are saved to the current working directory.',
@@ -304,7 +343,9 @@
' Options:', ' Options:',
'', '',
' -c, --stdout Write output to standard output', ' -c, --stdout Write output to standard output',
' -d, --debug Write only the debug output',
' -h, --help Display help information', ' -h, --help Display help information',
' -m, --minify Write only the minified output',
' -o, --output Write output to a given path/filename', ' -o, --output Write output to a given path/filename',
' -s, --silent Skip status updates normally logged to the console', ' -s, --silent Skip status updates normally logged to the console',
' -V, --version Output current version of Lo-Dash', ' -V, --version Output current version of Lo-Dash',
@@ -734,7 +775,7 @@
* Creates a debug and minified build, executing the `callback` for each. * Creates a debug and minified build, executing the `callback` for each.
* The `callback` is invoked with 2 arguments; (filepath, source) * The `callback` is invoked with 2 arguments; (filepath, source)
* *
* @param {Array} options The options array. * @param {Array} options The build options array.
* @param {Function} callback The function called per build. * @param {Function} callback The function called per build.
*/ */
function build(options, callback) { function build(options, callback) {
@@ -746,7 +787,7 @@
// used to report invalid command-line arguments // used to report invalid command-line arguments
var invalidArgs = _.reject(options.slice(options[0] == 'node' ? 2 : 0), function(value, index, options) { var invalidArgs = _.reject(options.slice(options[0] == 'node' ? 2 : 0), function(value, index, options) {
if (/^(?:-o|--output)$/.test(options[index - 1]) || if (/^(?:-o|--output)$/.test(options[index - 1]) ||
/^(?:category|exclude|exports|iife|include|minus|plus|template)=.*$/.test(value)) { /^(?:category|exclude|exports|iife|include|minus|plus|settings|template)=.*$/.test(value)) {
return true; return true;
} }
return [ return [
@@ -757,7 +798,9 @@
'strict', 'strict',
'underscore', 'underscore',
'-c', '--stdout', '-c', '--stdout',
'-d', '--debug',
'-h', '--help', '-h', '--help',
'-m', '--minify',
'-o', '--output', '-o', '--output',
'-s', '--silent', '-s', '--silent',
'-V', '--version' '-V', '--version'
@@ -798,7 +841,8 @@
// used to specify a custom IIFE to wrap Lo-Dash // used to specify a custom IIFE to wrap Lo-Dash
var iife = options.reduce(function(result, value) { var iife = options.reduce(function(result, value) {
return result || (result = value.match(/iife=(.*)/)) && result[1]; var match = value.match(/iife=(.*)/);
return match ? match[1] : result;
}, null); }, null);
// flag used to specify a Backbone build // flag used to specify a Backbone build
@@ -807,12 +851,18 @@
// flag used to specify a Content Security Policy build // flag used to specify a Content Security Policy build
var isCSP = options.indexOf('csp') > -1 || options.indexOf('CSP') > -1; var isCSP = options.indexOf('csp') > -1 || options.indexOf('CSP') > -1;
// flag used to specify only creating the debug build
var isDebug = options.indexOf('-d') > -1 || options.indexOf('--debug') > -1;
// flag used to specify a legacy build // flag used to specify a legacy build
var isLegacy = options.indexOf('legacy') > -1; var isLegacy = options.indexOf('legacy') > -1;
// flag used to specify an Underscore build // flag used to specify an Underscore build
var isUnderscore = options.indexOf('underscore') > -1; var isUnderscore = options.indexOf('underscore') > -1;
// flag used to specify only creating the minified build
var isMinify = !isDebug && options.indexOf('-m') > -1 || options.indexOf('--minify')> -1;
// flag used to specify a mobile build // flag used to specify a mobile build
var isMobile = !isLegacy && (isCSP || isUnderscore || options.indexOf('mobile') > -1); var isMobile = !isLegacy && (isCSP || isUnderscore || options.indexOf('mobile') > -1);
@@ -836,9 +886,32 @@
// used to specify the output path for builds // used to specify the output path for builds
var outputPath = options.reduce(function(result, value, index) { var outputPath = options.reduce(function(result, value, index) {
return result || (/^(?:-o|--output)$/.test(value) ? options[index + 1] : result); if (/-o|--output/.test(value)) {
result = options[index + 1];
result = path.join(fs.realpathSync(path.dirname(result)), path.basename(result));
}
return result;
}, ''); }, '');
// used to match external template files to pre-compile
var templatePattern = options.reduce(function(result, value) {
var match = value.match(/template=(.+)$/);
return match
? path.join(fs.realpathSync(path.dirname(match[1])), path.basename(match[1]))
: result;
}, '');
// used when pre-compiling template files
var templateSettings = options.reduce(function(result, value) {
var match = value.match(/settings=(.+)$/);
return match
? JSON.parse(match[1])
: result;
}, _.clone(_.templateSettings));
// flag used to specify a template build
var isTemplate = !!templatePattern;
// the lodash.js source // the lodash.js source
var source = fs.readFileSync(path.join(__dirname, 'lodash.js'), 'utf8'); var source = fs.readFileSync(path.join(__dirname, 'lodash.js'), 'utf8');
@@ -847,33 +920,34 @@
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/
// collections of method names // names of methods to include in the build
var buildMethods; var buildMethods = !isTemplate && (function() {
var result;
var minusMethods = options.reduce(function(result, value) { var minusMethods = options.reduce(function(accumulator, value) {
return /exclude|minus/.test(value) return /exclude|minus/.test(value)
? _.union(result, optionToMethodsArray(source, value)) ? _.union(accumulator, optionToMethodsArray(source, value))
: result; : accumulator;
}, []); }, []);
var plusMethods = options.reduce(function(result, value) { var plusMethods = options.reduce(function(accumulator, value) {
return /plus/.test(value) return /plus/.test(value)
? _.union(result, optionToMethodsArray(source, value)) ? _.union(accumulator, optionToMethodsArray(source, value))
: result; : accumulator;
}, []); }, []);
// add method names explicitly // add method names explicitly
options.some(function(value) { options.some(function(value) {
return /include/.test(value) && return /include/.test(value) &&
(buildMethods = getDependencies(optionToMethodsArray(source, value))); (result = getDependencies(optionToMethodsArray(source, value)));
}); });
// add method names required by Backbone and Underscore builds // add method names required by Backbone and Underscore builds
if (isBackbone && !buildMethods) { if (isBackbone && !result) {
buildMethods = getDependencies(backboneDependencies); result = getDependencies(backboneDependencies);
} }
if (isUnderscore && !buildMethods) { if (isUnderscore && !result) {
buildMethods = getDependencies(underscoreMethods); result = getDependencies(underscoreMethods);
} }
// add method names by category // add method names by category
@@ -882,30 +956,32 @@
return false; return false;
} }
// resolve method names belonging to each category (case-insensitive) // resolve method names belonging to each category (case-insensitive)
var methodNames = optionToArray(value).reduce(function(result, category) { var methodNames = optionToArray(value).reduce(function(accumulator, category) {
var capitalized = category[0].toUpperCase() + category.toLowerCase().slice(1); var capitalized = category[0].toUpperCase() + category.toLowerCase().slice(1);
return result.concat(getMethodsByCategory(source, capitalized)); return accumulator.concat(getMethodsByCategory(source, capitalized));
}, []); }, []);
return (buildMethods = _.union(buildMethods || [], getDependencies(methodNames))); return (result = _.union(result || [], getDependencies(methodNames)));
}); });
// init `buildMethods` if it hasn't been inited // init `result` if it hasn't been inited
if (!buildMethods) { if (!result) {
buildMethods = allMethods.slice(); result = allMethods.slice();
} }
if (plusMethods.length) { if (plusMethods.length) {
buildMethods = _.union(buildMethods, getDependencies(plusMethods)); result = _.union(result, getDependencies(plusMethods));
} }
if (minusMethods.length) { if (minusMethods.length) {
buildMethods = _.without.apply(_, [buildMethods].concat(minusMethods, getDependants(buildMethods))); result = _.without.apply(_, [result].concat(minusMethods, getDependants(result)));
} }
return result;
}());
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/
// load customized Lo-Dash module // load customized Lo-Dash module
var lodash = (function() { var lodash = !isTemplate && (function() {
var context = vm.createContext({ var context = vm.createContext({
'clearTimeout': clearTimeout, 'clearTimeout': clearTimeout,
'setTimeout': setTimeout 'setTimeout': setTimeout
@@ -980,6 +1056,10 @@
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/
if (isTemplate) {
source = buildTemplate(templatePattern, templateSettings);
}
else {
// simplify template snippets by removing unnecessary brackets // simplify template snippets by removing unnecessary brackets
source = source.replace( source = source.replace(
RegExp("{(\\\\n' *\\+\\s*.*?\\+\\n\\s*' *)}(?:\\\\n)?' *([,\\n])", 'g'), "$1'$2" RegExp("{(\\\\n' *\\+\\s*.*?\\+\\n\\s*' *)}(?:\\\\n)?' *([,\\n])", 'g'), "$1'$2"
@@ -1002,7 +1082,7 @@
source = removeIsArgumentsFallback(source); source = removeIsArgumentsFallback(source);
} }
/*------------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/
if (isLegacy) { if (isLegacy) {
_.each(['isBindFast', 'nativeBind', 'nativeIsArray', 'nativeKeys'], function(varName) { _.each(['isBindFast', 'nativeBind', 'nativeIsArray', 'nativeKeys'], function(varName) {
@@ -1066,7 +1146,22 @@
return match.replace(/\bcallee\b/g, 'merge'); return match.replace(/\bcallee\b/g, 'merge');
}); });
if (!isUnderscore) { if (isUnderscore) {
(function() {
// replace `isArguments` and its fallback
var snippet = matchFunction(source, 'isArguments')
.replace(/function isArguments/, 'lodash.isArguments = function');
source = removeFunction(source, 'isArguments');
source = source.replace(getIsArgumentsFallback(source), function(match) {
return snippet + '\n' + match
.replace(/\bisArguments\b/g, 'lodash.$&')
.replace(/\bnoArgsClass\b/g, '!lodash.isArguments(arguments)');
});
}());
}
else {
source = removeIsArgumentsFallback(source); source = removeIsArgumentsFallback(source);
} }
@@ -1130,6 +1225,22 @@
return '$1' + snippet + ';\n'; return '$1' + snippet + ';\n';
}())); }()));
} }
}
/*------------------------------------------------------------------------*/
// customize Lo-Dash's IIFE
(function() {
if (typeof iife == 'string') {
var token = '%output%',
index = iife.indexOf(token);
source = source.match(/\/\*![\s\S]+?\*\/\n/) +
iife.slice(0, index) +
source.replace(/^[^(]+?\(function[^{]+?{|}\(this\)\)[;\s]*$/g, '') +
iife.slice(index + token.length);
}
}());
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/
@@ -1156,21 +1267,10 @@
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/
// customize Lo-Dash's IIFE if (isTemplate) {
(function() { debugSource = source;
if (typeof iife == 'string') {
var token = '%output%',
index = iife.indexOf(token);
source = source.match(/\/\*![\s\S]+?\*\/\n/) +
iife.slice(0, index) +
source.replace(/^[^(]+?\(function[^{]+?{|}\(this\)\)[;\s]*$/g, '') +
iife.slice(index + token.length);
} }
}()); else {
/*------------------------------------------------------------------------*/
// modify/remove references to removed methods/variables // modify/remove references to removed methods/variables
if (isRemoved(source, 'isArguments')) { if (isRemoved(source, 'isArguments')) {
source = replaceVar(source, 'noArgsClass', 'false'); source = replaceVar(source, 'noArgsClass', 'false');
@@ -1213,12 +1313,6 @@
if (isRemoved(source, 'toArray')) { if (isRemoved(source, 'toArray')) {
source = removeVar(source, 'noArraySliceOnStrings'); source = removeVar(source, 'noArraySliceOnStrings');
} }
if (isUnderscore
? isRemoved(source, 'merge')
: isRemoved(source, 'clone', 'merge')
) {
source = removeFunction(source, 'isPlainObject');
}
if (isRemoved(source, 'clone', 'isArguments', 'isEmpty', 'isEqual')) { if (isRemoved(source, 'clone', 'isArguments', 'isEmpty', 'isEqual')) {
source = removeNoArgsClass(source); source = removeNoArgsClass(source);
} }
@@ -1253,9 +1347,7 @@
// remove `hasDontEnumBug`, `hasObjectSpliceBug`, `iteratesOwnLast`, and `noArgsEnum` assignment // remove `hasDontEnumBug`, `hasObjectSpliceBug`, `iteratesOwnLast`, and `noArgsEnum` assignment
source = source.replace(/ *\(function\(\) *{[\s\S]+?}\(1\)\);/, ''); source = source.replace(/ *\(function\(\) *{[\s\S]+?}\(1\)\);/, '');
} }
}
debugSource = cleanupSource(debugSource);
source = cleanupSource(source);
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/
@@ -1265,16 +1357,23 @@
!_.isEqual(exportsOptions, exportsAll); !_.isEqual(exportsOptions, exportsAll);
// used to name temporary files created in `dist/` // used to name temporary files created in `dist/`
var workingName = 'lodash' + (isCustom ? '.custom' : '') + '.min'; var workingName = 'lodash' + (isTemplate ? '.template' : isCustom ? '.custom' : '');
// restore `dependencyMap` // restore `dependencyMap`
dependencyMap = dependencyBackup; dependencyMap = dependencyBackup;
// output debug build // output debug build
if (isCustom && !outputPath && !isStdOut) { if (!isMinify && (isCustom || isTemplate)) {
callback(debugSource, path.join(cwd, 'lodash.custom.js')); if (isDebug && isStdOut) {
stdout.write(debugSource);
callback(debugSource);
} else if (!isStdOut) {
callback(debugSource, (isDebug && outputPath) || path.join(cwd, workingName + '.js'));
}
} }
// begin the minification process // begin the minification process
if (!isDebug) {
workingName += '.min';
minify(source, { minify(source, {
'silent': isSilent, 'silent': isSilent,
'workingName': workingName, 'workingName': workingName,
@@ -1295,6 +1394,7 @@
} }
}); });
} }
}
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/

View File

@@ -175,6 +175,7 @@
'isNull', 'isNull',
'isNumber', 'isNumber',
'isObject', 'isObject',
'isPlainObject',
'isRegExp', 'isRegExp',
'isString', 'isString',
'isUndefined', 'isUndefined',
@@ -217,6 +218,7 @@
'take', 'take',
'tap', 'tap',
'template', 'template',
'templates',
'templateSettings', 'templateSettings',
'throttle', 'throttle',
'times', 'times',

View File

@@ -62,7 +62,7 @@
} }
}, },
['lodash', 'shimmed', 'underscore'], function(lodash, shimmed, underscore) { ['lodash', 'shimmed', 'underscore'], function(lodash, shimmed, underscore) {
if (lodash.noConflict) { if (lodash && lodash.noConflict) {
lodashModule = lodash.noConflict(); lodashModule = lodash.noConflict();
lodashModule.moduleName = 'lodash'; lodashModule.moduleName = 'lodash';
} }
@@ -70,7 +70,7 @@
shimmedModule = shimmed.noConflict(); shimmedModule = shimmed.noConflict();
shimmedModule.moduleName = 'shimmed'; shimmedModule.moduleName = 'shimmed';
} }
if (underscore.noConflict) { if (underscore && underscore.noConflict) {
underscoreModule = underscore.noConflict(); underscoreModule = underscore.noConflict();
underscoreModule.moduleName = 'underscore'; underscoreModule.moduleName = 'underscore';
} }

View File

@@ -165,6 +165,7 @@
'isNull', 'isNull',
'isNumber', 'isNumber',
'isObject', 'isObject',
'isPlainObject',
'isRegExp', 'isRegExp',
'isString', 'isString',
'isUndefined', 'isUndefined',
@@ -238,19 +239,12 @@
/** List of methods used by Underscore */ /** List of methods used by Underscore */
var underscoreMethods = _.without.apply(_, [allMethods].concat([ var underscoreMethods = _.without.apply(_, [allMethods].concat([
'countBy',
'forIn', 'forIn',
'forOwn', 'forOwn',
'invert', 'isPlainObject',
'lateBind', 'lateBind',
'merge', 'merge',
'object', 'partial'
'omit',
'pairs',
'partial',
'random',
'unescape',
'where'
])); ]));
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
@@ -654,7 +648,7 @@
asyncTest('`lodash ' + command +'`', function() { asyncTest('`lodash ' + command +'`', function() {
build(['-s'].concat(command.split(' ')), function(source, filepath) { build(['-s'].concat(command.split(' ')), function(source, filepath) {
equal(filepath, 'a.js', command); equal(path.basename(filepath), 'a.js', command);
start(); start();
}); });
}); });
@@ -670,7 +664,7 @@
var start = _.once(QUnit.start); var start = _.once(QUnit.start);
asyncTest('`lodash ' + command +'`', function() { asyncTest('`lodash ' + command +'`', function() {
build([command, 'exports=', 'include='], function(source, filepath) { build([command, 'exports=', 'include='], function(source) {
equal(source, ''); equal(source, '');
equal(arguments.length, 1); equal(arguments.length, 1);
start(); start();

File diff suppressed because one or more lines are too long

View File

@@ -105,23 +105,16 @@
return results; return results;
}; };
// Internal data flag for performing `reduceRight`.
var right = null;
// **Reduce** builds up a single result from a list of values, aka `inject`, // **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
var initial = arguments.length > 2; var initial = arguments.length > 2;
if (obj == null) obj = []; if (obj == null) obj = [];
if (!right && nativeReduce && obj.reduce === nativeReduce) { if (nativeReduce && obj.reduce === nativeReduce) {
if (context) iterator = _.bind(iterator, context); if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
} }
each(obj, function(value, index, list) { each(obj, function(value, index, list) {
if (right) {
index = right.keys[index];
list = right.list;
}
if (!initial) { if (!initial) {
memo = value; memo = value;
initial = true; initial = true;
@@ -142,12 +135,22 @@
if (context) iterator = _.bind(iterator, context); if (context) iterator = _.bind(iterator, context);
return arguments.length > 2 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); return arguments.length > 2 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
} }
var values = _.toArray(obj).reverse(); var length = obj.length;
if (context && !initial) iterator = _.bind(iterator, context); if (length !== +length) {
right = {keys: _.keys(obj).reverse(), list: obj}; var keys = _.keys(obj);
var result = initial ? _.reduce(values, iterator, memo, context) : _.reduce(values, iterator); length = keys.length;
right = null; }
return result; each(obj, function(value, index, list) {
index = keys ? keys[--length] : --length;
if (!initial) {
memo = obj[index];
initial = true;
} else {
memo = iterator.call(context, memo, obj[index], index, list);
}
});
if (!initial) throw new TypeError('Reduce of empty array with no initial value');
return memo;
}; };
// Return the first value which passes a truth test. Aliased as `detect`. // Return the first value which passes a truth test. Aliased as `detect`.