#!/usr/bin/env node
'use strict';
var _ = require('lodash'),
glob = require('glob'),
semver = require('semver'),
vm = require('vm'),
consts = require('../lib/const'),
listing = require('../lib/listing'),
mapping = require('../lib/mapping'),
minify = require('../lib/minify'),
util = require('../lib/util');
var fs = util.fs,
Hash = util.Hash,
path = util.path;
var buildExports = listing.buildExports,
cwd = process.cwd(),
floor = Math.floor,
push = Array.prototype.push,
stdout = process.stdout,
uninlinables = listing.uninlinables;
/** Used to indicate whether this file is executed directly from Node.js. */
var isBin = module == require.main;
/*----------------------------------------------------------------------------*/
/**
* Adds build `commands` to the copyright header of `source`.
*
* @private
* @param {string} source The source to process.
* @param {Array} [commands=[]] An array of commands.
* @returns {string} Returns the modified source.
*/
function addCommandsToHeader(source, commands) {
return replace(source, getHeader(source), function(header) {
// Add quotes to commands with spaces or equals signs.
commands = _.map(commands, function(command) {
var separator = /[= ]/.exec(command);
if (separator) {
separator = separator[0];
var parts = command.split(separator);
command = parts[0] + separator + '"' + parts.slice(1).join(separator) + '"';
}
// Escape newlines, carriage returns, multi-line comment end tokens.
return command
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
.replace(/\*\//g, '*\\/');
});
// Remove any existing custom build information.
header = header
.replace(' (Custom Build)', '')
.replace(/^ *\* *Build:.+\n/m, '');
// Add build commands to copyright header.
return header.replace(/(\/\**\n)( \*)( *@license[\s*]+)?( *Lodash)(.*)/, function(match, prelude, indent, tag, title, postlude) {
return (
prelude +
indent +
(tag || '') +
title + ' (Custom Build)' + postlude + '\n' +
indent + ' Build: `lodash ' + commands.join(' ') + '`'
);
});
});
}
/**
* Creates modules based on the provided build state.
*
* @private
* @param {Object} state The build state object.
* @param {Function} [onComplete] The function called when all module builds
* are completed.
*/
function buildModule(state, onComplete) {
var buildFuncs = state.buildFuncs,
funcDepMap = state.funcDepMap,
includeFuncs = state.includeFuncs,
includeVars = state.includeVars,
isAMD = state.isAMD,
isES = state.isES,
isNode = state.isNode,
isNpm = state.isNpm,
isSilent = state.isSilent,
minusFuncs = state.minusFuncs,
outputPath = state.outputPath,
plusFuncs = state.plusFuncs,
stamp = state.stamp,
varDepMap = state.varDepMap;
var depMap = new Hash,
identifiers = _.without(_.union(buildFuncs, includeVars), 'main'),
moduleCount = 0,
removedDeps = new Hash;
var buildCallback = function(data) {
moduleCount++;
data.source = cleanupSource(addCommandsToHeader(data.source, state.options));
defaultBuildCallback(data);
};
var getUnusedDeps = function(source, depNames) {
source = cleanupSource(removeStrings(removeComments(source)));
return _.reject(depNames, function(depName) {
return RegExp('[^.$"\'\\w]' + depName + '\\b').test(source);
});
};
var toDepName = function(value) {
var prefix = (isPrivate(value) && !_.startsWith(value, '_')) ? '_' : '',
result = prefix + value;
return isNpm ? ('lodash.' + result.toLowerCase()) : result;
};
var toDepPath = function(value) {
return './' + toDepName(value) + (isES ? '.js' : '');
};
if (isNpm) {
// Exclude "Seq" methods and most internal functions when exporting for npm.
identifiers = _.reject(_.difference(identifiers, mapping.category.Seq), function(identifier) {
return isPrivate(identifier) && !_.includes(uninlinables, identifier) && !_.includes(includeFuncs, identifier);
});
// Remove unused packages.
var newPackages = _.map(identifiers, toDepName),
oldPackages = _.map(glob.sync(path.join(outputPath, 'lodash.*')), _.ary(path.basename, 1)),
unusedPackages = _.difference(oldPackages, newPackages);
_.each(unusedPackages, function(packageName) {
var pathname = path.join(outputPath, packageName);
_.each(fs.readdirSync(pathname), function(identifier) {
fs.unlinkSync(path.join(pathname, identifier));
});
fs.rmdirSync(pathname);
});
}
// List of identifiers that keep their copyright headers.
var includeHeaders = isNpm ? identifiers : _.difference(_.union(includeFuncs, plusFuncs), minusFuncs);
if (_.isEmpty(includeHeaders)) {
includeHeaders = ['main'];
}
// Sort identifiers so private packages with the least number of interdependent
// dependencies are first.
identifiers = _.orderBy(identifiers, [
isPrivate,
function(identifier) {
var depNames = _.union(
getDependencies(identifier, funcDepMap),
getDependencies(identifier, varDepMap)
);
var inlinees = [identifier];
if (isNpm) {
var moreInlinees = _.transform(_.difference(depNames, uninlinables), function(result, identifier) {
push.apply(result, getAllDependencies([identifier], funcDepMap, varDepMap, _.clone(uninlinables)));
return result;
});
depNames = _.union(_.intersection(uninlinables, depNames), _.intersection(uninlinables, moreInlinees));
inlinees = inlinees.concat(_.difference(moreInlinees, depNames));
}
// Populate `depMap`.
depMap[identifier] = new Hash({
'depNames': depNames,
'inlinees': inlinees,
'isPrivate': isPrivate(identifier)
});
return depNames.length;
},
function(identifier) {
if (!depMap[identifier].isPrivate) {
return true;
}
return _.isString(_.findKey(depMap, function(value, key) {
var data = depMap[key];
return data.isPrivate && _.includes(data.depNames, identifier);
}));
}],
['desc']
);
// Create modules for each identifier.
_.each(identifiers, function(identifier) {
var basename = _.get(mapping.forceAlias, identifier, identifier),
category = getCategory(identifier),
depData = depMap[identifier],
depName = toDepName(basename),
depNames = depData.depNames,
inlinees = depData.inlinees;
if (isNpm) {
var inlineFuncs = _.intersection(listing.funcs, inlinees),
inlineVars = _.intersection(listing.varDeps, inlinees);
}
else {
inlineFuncs = _.includes(listing.funcs, identifier) ? inlinees : [];
inlineVars = _.includes(listing.varDeps, identifier) ? inlinees : [];
}
state.outputPath = path.join(outputPath, isNpm ? (depName + '/index.js') : (depName + '.js'));
state.buildFuncs = state.includeFuncs = inlineFuncs;
state.includeVars = inlineVars;
build(state, function(data) {
var iife = [],
source = data.source,
unusedDeps = getUnusedDeps(source, depNames);
// Track and remove unused dependencies.
removedDeps[identifier] = unusedDeps;
depNames = _.sortBy(_.difference(depNames, unusedDeps), toDepName);
depNames = _.map(depNames, function(depName) {
return _.get(mapping.forceAlias, depName, depName);
}).sort();
var depPaths = isNpm
? _.map(depNames, toDepName)
: _.map(depNames, toDepPath);
if (isAMD) {
var depArgs = _.map(depNames, function(depName) {
return _.get(mapping.wrapperToReal, depName, depName);
});
iife.push(
'define([' + (_.isEmpty(depPaths) ? '' : "'" + depPaths.join("', '") + "'") + '], function(' + depArgs.join(', ') + ') {',
'%output%',
' return ' + identifier + ';',
'});'
);
}
else if (isES) {
iife.push(
_.map(depPaths, function(depPath, index) {
var depName = depNames[index],
varName = _.get(mapping.wrapperToReal, depName, depName);
return 'import ' + varName + " from '" + depPath + "';";
})
.join('\n'),
'%output%',
'export default ' + identifier + ';'
);
}
else {
iife.push(
_.map(depPaths, function(depPath, index) {
var depName = depNames[index],
varName = _.get(mapping.wrapperToReal, depName, depName),
lastIndex = depPaths.length - 1;
return (index ? '' : 'var ') +
varName + " = require('" + depPath + "')" +
(index == lastIndex ? ';' : '');
})
.join(',\n '),
'%output%',
'module.exports = ' + identifier + ';'
);
}
if (!isAMD) {
source = replaceIndent(source, 0, 1);
}
source = _.includes(includeHeaders, identifier) ? removeLicenseTag(source) : removeHeader(source);
source = replaceIIFE(source, iife.join('\n'));
if (isNpm) {
var templatePath = fs.realpathSync(path.join(__dirname, '..', 'template')),
licenseTemplate = fs.readFileSync(path.join(templatePath, 'license.jst'), 'utf8'),
packageTemplate = fs.readFileSync(path.join(templatePath, 'package.jst'), 'utf8'),
readmeTemplate = fs.readFileSync(path.join(templatePath, 'readme.jst'), 'utf8');
var type = 'function',
pkgVer = semver.parse(state.lodash.VERSION),
pkgDepNames = _.sortBy(depNames, toDepName),
pkgDepPaths = _.sortBy(depPaths);
if (_.includes(listing.varDeps, identifier)) {
type = 'variable';
}
var templateData = {
'identifier': identifier,
'name': depName,
'type': type,
'version': pkgVer.raw
};
templateData.dependencies = _.transform(pkgDepNames, function(result, depName, index) {
var minor = '0',
depPath = pkgDepPaths[index],
depRange = '^' + pkgVer.major + '.0.0',
oldDepPkgPath = path.join(path.resolve(outputPath), depPath, 'package.json');
if (fs.existsSync(oldDepPkgPath)) {
var oldDepPkg = JSON.parse(fs.readFileSync(oldDepPkgPath, 'utf8')),
oldDepVer = semver.parse(oldDepPkg.version);
depRange = '^' + oldDepVer.major + '.' + minor + '.0';
if (oldDepVer.major > pkgVer.major) {
pkgVer = oldDepVer;
templateData.version = pkgVer.major + '.0.0';
}
}
result[depPath] = depRange;
}, {});
// Independently update the package version.
if (fs.existsSync(data.outputPath)) {
var laxDeps = _.map(listing.laxSemVerDeps, toDepName),
oldPkgPath = path.join(path.resolve(outputPath), depName, 'package.json'),
oldPkg = JSON.parse(fs.readFileSync(oldPkgPath, 'utf8')),
oldVer = oldPkg.version,
oldDeps = _.omit(oldPkg.dependencies, laxDeps),
pkgDeps = _.omit(templateData.dependencies, laxDeps);
if (_.isEqual(pkgDeps, oldDeps)) {
var oldSource = fs.readFileSync(data.outputPath, 'utf8');
// Exit early if sources are identical.
if (removeHeader(cleanupSource(oldSource)) === removeHeader(cleanupSource(source))) {
return;
}
// Bump the `patch` version if the source has changed.
templateData.version = cleanupSource(removeComments(oldSource)) === cleanupSource(removeComments(source))
? oldVer
: semver.inc(oldVer, 'patch', true);
}
else {
// Bump the `minor` version if the dependencies have changed.
templateData.version = semver.inc(oldVer, 'minor', true);
}
var isSameVersion = templateData.version == oldVer;
source = source.replace(getHeader(source), function(header) {
// Use the old header if the package version is unchanged,
// otherwise remove the version from the header.
return isSameVersion
? getHeader(oldSource)
: header;
});
}
if (!isSameVersion) {
var oldPath = path.join(outputPath, depName, 'LICENSE.txt');
if (fs.existsSync(oldPath)) {
fs.unlinkSync(oldPath);
}
fs.writeFileSync(path.join(outputPath, depName, 'package.json'), _.template(packageTemplate)(templateData));
fs.writeFileSync(path.join(outputPath, depName, 'LICENSE'), _.template(licenseTemplate)(templateData));
fs.writeFileSync(path.join(outputPath, depName, 'README.md'), _.template(readmeTemplate)(templateData));
}
}
data.source = source;
buildCallback(data);
});
});
// Add alias modules.
_.each(!isNpm && identifiers, function(identifier) {
var basename = _.get(mapping.forceAlias, identifier, identifier),
aliases = _.without(getAliases(identifier), basename),
category = getCategory(identifier),
depName = toDepName(basename),
depPath = toDepPath(basename);
_.each(aliases, function(alias) {
var iife = [];
if (isAMD) {
iife.push(
'define(["' + depPath + '"], function(' + depName + ') {',
' return ' + depName + ';',
'});'
);
}
else if (isES) {
iife.push(
"export { default } from '" + depPath + "'"
);
}
else {
iife.push(
'module.exports' +
" = require('" + depPath + "');"
);
}
buildCallback({
'outputPath': path.join(outputPath, alias + '.js'),
'source': iife.join('\n')
});
});
});
// Create main module.
_.times(isES ? 2 : 1, function(index) {
var identifier = 'main';
if (isNpm || !_.includes(buildFuncs, identifier)) {
return;
}
var categories = _.uniq(_.compact(_.map(identifiers, function(identifier) {
return getCategory(identifier);
}))).sort();
var categoryDeps = _.map(categories, function(category) {
return mapping.categoryToDepName[category] || category.toLowerCase();
});
var categoryDepPaths = _.map(categories, function(category) {
return toDepPath(category).toLowerCase();
});
var deps = _.union(
getDependencies(identifier, funcDepMap),
getDependencies(identifier, varDepMap)
);
var basename = 'lodash';
if (isAMD) {
basename = 'main';
} else if (isES) {
basename += (index ? '.default' : '');
}
state.buildFuncs = state.includeFuncs = [identifier];
state.outputPath = path.join(outputPath, basename + '.js');
build(state, function(data) {
var source = data.source;
// Remove unused method and alias assignments.
_.each(_.difference(listing.funcs, buildFuncs), function(funcName) {
source = removeMethodAssignment(source, funcName);
});
// Wrap `_.mixin`.
source = source.replace(/^(?: *\/\/.*\n)* *lodash\.[$\w]+\s*=[^;]+;\n/m, function(match) {
return [
' // wrap `_.mixin` so it works when provided only one argument',
' ' + (isES ? 'var ' : '') + 'mixin = (function(func) {',
' return function(object, source, options) {',
' if (options == null) {',
' var isObj = isObject(source),',
' props = isObj && keys(source),',
' methodNames = props && props.length && baseFunctions(source, props);',
'',
' if (!(methodNames ? methodNames.length : isObj)) {',
' options = source;',
' source = object;',
' object = this;',
' }',
' }',
' return func(object, source, options);',
' };',
' }(' + (isES ? '_' : '') + 'mixin));',
'',
match
].join('\n');
});
// Add `lodash.templateSettings` and placeholder assignments.
source = source.replace(/^ *lodash\.VERSION\b.+\n/m, function(match) {
var code = [];
if (_.includes(identifiers, 'templateSettings')) {
code.push(' (lodash.templateSettings = ' + mapping.categoryToDepName.String + '.templateSettings).imports._ = lodash;');
}
var funcNames = _.intersection(buildFuncs, listing.placeholderFuncs);
if (!_.isEmpty(funcNames)) {
code.push(
'',
' // Assign default placeholders.'
);
if (_.size(funcNames) > 1) {
code.push(
" arrayEach(['" + funcNames.join("', '") + "'], function(methodName) {",
' lodash[methodName].placeholder = lodash;',
' });'
);
} else {
code.push(' lodash.' + funcNames[0] + '.placeholder = lodash;');
}
}
code.push('');
return match + code.join('\n');
});
// Add category namespaces to each lodash function assignment.
source = source.replace(/(lodash(?:\.prototype)?(?:\[[$\w]+\]|\.[$\w]+)\s*=\s*)(?!lodash\b)([$\w]+)/g, function(match, left, identifier) {
if (_.includes(deps, identifier)) {
return match;
}
var category = mapping.categoryToDepName[getCategory(identifier)];
identifier = _.get(mapping.wrapperToReal, identifier, identifier);
return left + (category ? category + '.' : '') + identifier;
});
// Track and remove unused dependencies.
var unusedDeps = getUnusedDeps(source, deps);
removedDeps[identifier] = unusedDeps;
deps = _.difference(deps, unusedDeps);
deps = _.map(deps, function(depName) {
return _.get(mapping.forceAlias, depName, depName);
}).sort();
var depNames = categoryDeps.concat(deps);
if (isES) {
// Avoid a syntax error caused by reassigning `mixin` by naming the
// dependency `_mixin` instead.
depNames[_.indexOf(depNames, 'mixin')] = '_mixin';
}
var iife = [];
var depArgs = _.map(depNames, function(depName) {
return _.get(mapping.wrapperToReal, depName, depName);
});
var depPaths = categoryDepPaths.concat(_.map(deps, toDepPath));
if (isAMD) {
iife.push(
'define([' + (_.isEmpty(depPaths) ? '' : "'" + depPaths.join("', '") + "'") + '], function(' + depArgs.join(', ') + ') {',
'%output%',
' return lodash;',
'});'
);
}
else if (isES) {
if (index) {
iife.push(
_.map(depPaths, function(depPath, index) {
var depName = depNames[index],
varName = _.get(mapping.wrapperToReal, depName, depName);
return 'import ' + varName + " from '" + depPath + "';";
}).join('\n'),
'%output%',
'export default lodash;'
);
}
else {
deps = _.reject(identifiers, isPrivate);
var depData = _.sortBy(_.transform(deps, function(result, depName) {
var aliases = getAliases(depName),
forceAlias = mapping.forceAlias[depName],
dataName = forceAlias === undefined ? depName : forceAlias,
dataPath = toDepPath(_.includes(aliases, forceAlias) ? forceAlias : dataName);
result.push({ 'name': depName, 'path': dataPath });
push.apply(result, _.map(aliases, function(alias) {
return { 'name': alias, 'path': toDepPath(alias) };
}));
}), 'name');
depNames = _.map(depData, 'name');
depPaths = _.map(depData, 'path');
iife.push(
_.map(depPaths, function(depPath, index) {
var depName = depNames[index];
return 'export { default as ' + depName + " } from '" + depPath + "';";
}).join('\n'),
"export { default } from './lodash.default.js';"
);
}
}
else {
iife.push(
_.map(depPaths, function(depPath, index) {
var depName = depNames[index],
varName = _.get(mapping.wrapperToReal, depName, depName),
lastIndex = depPaths.length - 1;
return (index ? '' : 'var ') +
varName + " = require('" + depPath + "')" +
(index == lastIndex ? ';' : '');
}).join(',\n '),
'%output%',
'module.exports = lodash;'
);
}
if (!isAMD) {
source = replaceIndent(source, 0, 1);
}
if (!_.includes(includeHeaders, identifier)) {
source = removeHeader(source);
}
source = replaceIIFE(source, iife.join('\n'));
if (isNode) {
fs.writeFileSync(path.join(outputPath, 'index.js'), "module.exports = require('./lodash');");
}
data.source = source;
buildCallback(data);
});
});
// Create category modules.
_.each(!isNpm && _.uniq(_.compact(_.flatten(_.map(identifiers, getCategory)))), function(category) {
_.times(isES ? 2 : 1, function(index) {
var basename = category.toLowerCase(),
deps = _.intersection(getNamesByCategory(category), identifiers);
var depData = _.sortBy(_.transform(deps, function(result, depName) {
var forceAlias = mapping.forceAlias[depName],
aliases = _.without(getAliases(depName), forceAlias, mapping.wrapperToReal[depName]),
dataName = forceAlias === undefined ? depName : forceAlias,
dataPath = toDepPath(_.includes(aliases, forceAlias) ? forceAlias : dataName);
dataName = _.get(mapping.wrapperToReal, dataName, dataName);
result.push({ 'name': dataName, 'path': dataPath });
push.apply(result, _.map(aliases, function(alias) {
return { 'name': alias, 'path': toDepPath(alias) };
}));
}), 'name');
var depNames = _.map(depData, 'name'),
depPaths = _.map(depData, 'path'),
iife = [];
if (isAMD) {
iife.push(
"define(['" + depPaths.join("', '") + "'], function(" + depNames.join(', ') + ') {',
' return {',
_.map(depNames, function(depName) {
var key = _.get(mapping.wrapperToReal, depName, depName);
return " '" + key + "': " + depName;
})
.join(',\n'),
' };',
'});'
);
}
else {
if (isES) {
if (index) {
iife.push(
_.map(depNames, function(depName, index) {
return 'import ' + depName + " from '" + depPaths[index] + "';";
})
.join('\n'),
'',
'export default {',
_(depNames)
.chunk(5)
.map(function(chunk) { return " " + chunk.join(', '); })
.join(',\n'),
'};'
);
}
else {
iife.push(
'',
_.map(depNames, function(depName, index) {
var key = _.get(mapping.wrapperToReal, depName, depName);
return 'export { default as ' + key + " } from '" + depPaths[index] + "';";
})
.join('\n'),
"export { default } from './" + basename + ".default.js';"
);
}
}
else {
iife.push(
'module.exports = {',
_.map(depNames, function(depName) {
var depPath = depPaths[_.indexOf(depNames, depName)],
key = _.get(mapping.wrapperToReal, depName, depName);
return " '" + key + "': require('" + depPath + "')";
})
.join(',\n'),
'};'
);
}
}
buildCallback({
'outputPath': path.join(outputPath, basename + (index ? '.default' : '') + '.js'),
'source': iife.join('\n')
});
});
});
if (!isSilent) {
// Warn of removed dependencies.
_.forOwn(removedDeps, function(depNames, identifier) {
if (!_.isEmpty(depNames)) {
var plural = _.size(depNames) > 1;
console.warn('Warning: Removed ' + (plural ? '' : 'an ') + 'unused dependenc' + (plural ? 'ies' : 'y') + ' from `' + identifier + '`: ' + depNames.sort().join(', '));
}
});
console.log('Created %d modules in %d seconds.', moduleCount, (_.now() - stamp) / 1000);
}
if (onComplete) {
onComplete({ 'outputPath': fs.realpathSync(outputPath) });
}
}
/**
* Compiles template files based on the provided build state extending
* `_.templates` with precompiled templates named after each file's basename.
*
* @private
* @param {Object} state The build state object.
* @returns {string} Returns the compiled source.
*/
function buildTemplate(state) {
var moduleId = state.moduleId || 'lodash',
isStandalone = moduleId == 'none',
pattern = state.templatePattern,
settings = state.templateSettings;
pattern = path.normalize(pattern || path.join(cwd, '*.jst'));
var source = [
';(function() {',
' var undefined;',
'',
" var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;",
'',
" var freeGlobalThis = typeof globalThis == 'object' && globalThis !== null && globalThis.Object == Object && globalThis;",
'',
" var freeSelf = typeof self == 'object' && self && self.Object === Object && self;",
'',
" var root = freeGlobalThis || freeGlobal || freeSelf || Function('return this')();",
'',
" var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;",
'',
" var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;",
''
];
if (isStandalone) {
build(['exports=none', 'include=escape', 'iife=%output%', '-d', '-s'], function(data) {
var escapeSource = data.source;
escapeSource = removeHeader(escapeSource);
escapeSource = removeFunction(escapeSource, 'lodash');
escapeSource = removeAssignments(escapeSource);
escapeSource = cleanupSource(escapeSource);
source.push(
'',
escapeSource,
'',
" var _ = { 'escape': escape };",
''
);
});
}
else {
source.push(
' var _ = root._ || {};',
''
);
}
source.push(
consts.hr,
''
);
var dirname = path.dirname(pattern),
filePaths = glob.sync(pattern);
if (dirname == '.') {
dirname = '';
}
var basePath = (dirname + path.sep).replace(RegExp('(^|' + path.sepEscaped + ')\\*\\*.*$'), '$1'),
insertAt = source.length,
templates = new Hash;
_.each(filePaths, function(filePath) {
var string = fs.readFileSync(filePath, 'utf8'),
precompiled = cleanupCompiled(getFunctionSource(_.template(string, settings), 2));
// Glob uses *nix path separators even on Windows.
// See https://github.com/isaacs/node-glob#windows.
var clipped = filePath.slice(dirname ? basePath.length : 0).replace(/\..*$/, ''),
props = clipped.split('/');
// Create namespace objects.
_.reduce(props, function(object, key) {
return object[key] || (object[key] = new Hash);
}, templates);
// Escape namespace property names.
props = _.map(props, function(key) {
return "['" + key.replace(/['\n\r\t]/g, '\\$&') + "']";
});
// Add template assignment to `source`.
source.push(' templates' + props.join('') + ' = ' + precompiled + ';', '');
});
// Add the initial `_.templates` object to `source`.
source.splice(insertAt, 0,
' var templates = ' +
JSON.stringify(templates, null, 4)
.replace(/^ *\}$/m, ' $&')
.replace(/'/g, "\\'")
.replace(/([^\\])"/g, "$1'") +
';',
''
);
source.push(
consts.hr,
'',
" if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {"
);
if (isStandalone) {
source.push(
' define(function() {',
' return templates;'
);
}
else {
source.push(
" define(['" + moduleId + "'], function(lodash) {",
' _ = lodash;',
' lodash.templates = lodash.extend(lodash.templates || {}, templates);'
);
}
source.push(
' });',
' }',
' else if (freeModule) {'
);
if (!isStandalone) {
source.push(" _ = require('" + moduleId + "');");
}
source.push(
' (freeModule.exports = templates).templates = templates;',
' freeExports.templates = templates;'
);
if (isStandalone) {
source.push(
' }',
' else {',
' root.templates = templates;',
' }'
);
}
else {
source.push(
' }',
' else if (_) {',
' _.templates = _.extend(_.templates || {}, templates);',
' }'
);
}
source.push('}.call(this));');
return source.join('\n');
}
/**
* Removes unnecessary semicolons and whitespace from compiled code.
*
* @private
* @param {string} source The source to process.
* @returns {string} Returns the modified source.
*/
function cleanupCompiled(source) {
return stringFree(source, function(source) {
return source
.replace(/\b(function)\s*(\()/g, '$1$2')
.replace(/([{}])\s*;/g, '$1');
});
}
/**
* Removes unnecessary comments, and whitespace.
*
* @private
* @param {string} source The source to process.
* @returns {string} Returns the modified source.
*/
function cleanupSource(source) {
return stringFree(source, function(source) {
return source
// Consolidate consecutive horizontal rule comment separators.
.replace(/(?:\s*\/\*-+\*\/\s*){2,}/g, function(separators) {
var indent = /^\s*/.exec(separators)[0];
return indent + separators.slice(separators.lastIndexOf('/*'));
})
// Remove unused single line comments.
.replace(/(\{\s*)?(\n *\/\/.*)(\s*\})/g, function(match, prelude, comment, postlude) {
return (!prelude && postlude) ? postlude : match;
})
// Remove unattached multi-line and single line comments.
.replace(/^ *(\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/|\/\/.*)\n\n/gm, function(match, comment, index) {
var isCopyright = !index && /\bcopyright\b/i.test(comment),
isHR = /^\/\*-+\*\/$/.test(comment);
return (isCopyright || isHR) ? match : '\n';
})
// Remove unused horizontal rule comment separators.
.replace(/(\{\s*\n) *\/\*-+\*\/\n|^ *\/\*-+\*\/\n(\s*\})/gm, '$1$2')
// Remove trailing horizontal rule comment separators.
.replace(/\s*\/\*-+\*\/\s*$/, '')
// Remove incomplete variable declarations.
.replace(/^ *var\s*;\n/gm, '')
// Remove lines with just spaces and semicolons.
.replace(/^ *;\n/gm, '')
// Remove trailing spaces from lines.
.replace(/ *$/gm, '')
// Consolidate multiple newlines.
.replace(/\n{3,}/g, '\n\n')
// Remove leading empty lines.
.replace(/^ *\n+/, '')
// Add trailing newline.
.trimRight() + '\n';
});
}
/**
* Invokes `callback` providing `source` with comments removed and returns the
* modified source with comments restored.
*
* @private
* @param {string} source The source to modify.
* @param {Function} [callback] The function to modify the comment free source.
* @returns {string} Returns the modified source.
*/
function commentFree(source, callback) {
var comments = [];
source = callback(replace(source, consts.reComment, function(match) {
var index = comments.length;
comments.push(match);
return '<#com_token' + index + '#>\n';
})) || '';
return replace(source, consts.reCommentToken, function(match) {
return comments[match.slice(11, -3)];
});
}
/**
* The default callback used for `build` invocations.
*
* @private
* @param {Object} data The data for the given build.
* gzip - The gzipped output of the built source
* outputPath - The path where the built source is to be written
* source - The built source output
* sourceMap - The source map output
*/
function defaultBuildCallback(data) {
var outputPath = data.outputPath,
sourceMap = data.sourceMap;
if (outputPath) {
fs.writeFileSync(outputPath, data.source);
if (sourceMap) {
fs.writeFileSync(path.join(path.dirname(outputPath), path.basename(outputPath, '.js') + '.map'), sourceMap);
}
}
}
/**
* Gets the aliases associated with a given identifier.
*
* @private
* @param {string} identifier The identifier to get aliases for.
* @returns {Array} Returns an array of aliases.
*/
function getAliases(identifier) {
return _.get(mapping.realToAlias, identifier, []);
}
/**
* Creates an array of all function, object, and variable dependencies for the
* given identifier(s).
*
* @private
* @param {string|string[]} identifier The identifier or array of identifiers to query.
* @param {Object} funcDepMap The dependency map to look up function dependencies.
* @param {Object} varDepMap The dependency map to look up variable dependencies.
* @param- {Array} [stackA=[]] Internally used track queried identifiers.
* @returns {Array} Returns an array of identifier dependencies.
*/
function getAllDependencies(identifier, funcDepMap, varDepMap, stack) {
var isInit = !stack,
result = _.isArray(identifier) ? (isInit ? _.clone(identifier) : identifier) : [identifier];
stack || (stack = []);
_.each(result, function(identifier) {
if (!_.includes(stack, identifier)) {
push.apply(result, getDependencies(identifier, funcDepMap));
push.apply(result, getDependencies(identifier, varDepMap));
stack.push(identifier);
getAllDependencies(result, funcDepMap, varDepMap, stack);
}
});
return isInit ? _.uniq(result) : result;
}
/**
* Gets the category of the given identifier.
*
* @private
* @param {string} identifier The identifier to query.
* @returns {string|undefined} Returns the category.
*/
function getCategory(identifier) {
identifier = getRealName(identifier);
return _.find(listing.categories, function(category) {
return _.includes(mapping.category[category], identifier);
});
}
/**
* Creates an array of depenants for the given identifier(s).
*
* @private
* @param {string} identifier The identifier or array of identifiers to query.
* @param {Object} depMap The dependency map to look up dependants.
* @param {boolean} [isDeep=false] A flag to specify retrieving nested dependants.
* @param- {Array} [stackA=[]] Internally used track queried identifiers.
* @returns {Array} Returns an array of identifier dependants.
*/
function getDependants(identifier, depMap, isDeep, stack) {
var isInit = !stack,
identifiers = _.isArray(identifier) ? identifier : [identifier];
stack || (stack = []);
// Iterate over the dependency map, adding names of functions that have `identifier` as a dependency.
var result = _.transform(depMap, function(result, depNames, otherName) {
if (!_.includes(stack, otherName) &&
_.some(identifiers, _.partial(_.includes, depNames, _, 0))) {
result.push(otherName);
if (isDeep) {
stack.push(otherName);
push.apply(result, getDependants(otherName, depMap, isDeep, stack));
}
}
}, []);
return isInit ? _.uniq(result) : result;
}
/**
* Creates an array of dependencies for the given identifier(s).
*
* @private
* @param {string|string[]} identifier The identifier or array of identifiers to query.
* @param {Object} depMap The dependency map to look up dependencies.
* @param {boolean} [isDeep=false] A flag to specify retrieving nested dependencies.
* @param- {Array} [stackA=[]] Internally used track queried identifiers.
* @returns {Array} Returns an array of identifier dependencies.
*/
function getDependencies(identifier, depMap, isDeep, stack) {
var isInit = !stack,
depNames = _.isArray(identifier) ? identifier : depMap[identifier];
stack || (stack = []);
if (!isDeep) {
return depNames ? _.difference(depNames, stack) : [];
}
// Recursively accumulate the dependencies of the `identifier` function,
// the dependencies of its dependencies, and so on.
var result = _.transform(depNames, function(result, otherName) {
if (!_.includes(stack, otherName)) {
stack.push(otherName);
result.push(otherName);
push.apply(result, getDependencies(otherName, depMap, isDeep, stack));
}
}, []);
return isInit ? _.uniq(result) : result;
}
/**
* Gets the formatted source of the given function.
*
* @private
* @param {Function} func The function to process.
* @param {number|string} [indent=0] The level to indent.
* @returns {string} Returns the formatted source.
*/
function getFunctionSource(func, indent) {
var source = toString(func.source || func),
srcIndent = getIndent(source),
forceIndent = _.size(source.match(RegExp('^' + srcIndent + '}', 'gm'))) > 1;
indent || (indent = '');
if (typeof indent == 'number') {
indent = _.repeat(' ', indent);
}
// Remove any existing indent.
if (srcIndent) {
source = source.replace(RegExp('^' + srcIndent, 'gm'), '');
}
// Set indent of source.
return indent + source.replace(/\n(?:.*)/g, function(match, index) {
var prelude = '\n' + indent;
match = match.slice(1);
if (forceIndent) {
prelude += (match == '}' && !_.includes(source, '}', index + 2) ? '' : ' ');
}
return prelude + match;
});
}
/**
* Gets the `getTag` fork from `source`.
*
* @private
* @param {string} source The source to inspect.
* @returns {string} Returns the fork.
*/
function getGetTagFork(source) {
return _.get(/^(?: *\/\/.*\n)*( *)if\s*\(\s*\(DataView\s*&&[\s\S]+?\n\1 getTag\s*=[\s\S]+?\n\1\}\n/m.exec(source), 0, '');
}
/**
* Gets the copyright header of `source`.
*
* @private
* @param {string} source The source to process.
* @returns {string} Returns the copyright header.
*/
function getHeader(source) {
source = toString(source);
return _.get(/^\s*\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/\n|^(?:\s*\/\/.*\n)+/.exec(source), 0, '');
}
/**
* Gets the indent string of the given function.
*
* @private
* @param {Function|string} func The function or function source to process.
* @returns {string} Returns the indent string.
*/
function getIndent(func) {
return _.get(/^ *(?=\S)/m.exec(func.source || func), 0, '');
}
/**
* Gets the lodash method assignments snippet from `source`.
*
* @private
* @param {string} source The source to inspect.
* @returns {string} Returns the method assignments snippet.
*/
function getMethodAssignments(source) {
source = toString(source);
return _.get(/\n\n(?: *\/\/.*\n)* *lodash(?:\.(?!prototype\b)[$\w]+)+\s*=[\s\S]+\n *lodash(?:\.[$\w]+)+\s=[^;]+;(?=\n)/.exec(source), 0, '');
}
/**
* Gets the names of identifiers in `source` that belong to the given category.
*
* @private
* @param {string} category The category to filter by.
* @returns {Array} Returns a new array of names.
*/
function getNamesByCategory(category) {
return _.get(mapping.category, category, []);
}
/**
* Gets the value of a given name from the `options` array. If no value is
* available the `defaultValue` is returned.
*
* @private
* @param {Array} options The options array to inspect.
* @param {string|string[]} names The name(s) of the option.
* @param {*} defaultValue The default option value.
* @returns {*} Returns the option value.
*/
function getOption(options, names, defaultValue) {
var isArr = _.isArray(defaultValue);
names = _.isArray(names) ? names : [names];
return _.reduce(options, function(result, option) {
_.each(names, function(name) {
if (isArr) {
var array = optionToArray(name, option);
result = _.isEmpty(array) ? result : array;
} else {
var value = optionToValue(name, option);
result = value == null ? result : value;
}
});
return result;
}, defaultValue);
}
/**
* Gets the real name of `alias`.
*
* @private
* @param {string} alias The alias to resolve.
* @returns {string} Returns the real name.
*/
function getRealName(alias) {
return _.get(mapping.aliasToReal, alias, alias);
}
/**
* Gets the real category of `alias`.
*
* @private
* @param {string} alias The alias to resolve.
* @returns {string} Returns the real category.
*/
function getRealCategory(alias) {
return _.get(mapping.oldCategory, alias, alias);
}
/**
* Creates an array variables names from all variables defined outside of
* lodash functions.
*
* @private
* @param {string} source The source to process.
* @returns {Array} Returns a new array of variable names.
*/
function getVars(source) {
var isDeep = consts.reHasDeepVars.test(source),
indentA = isDeep ? ' {2,4}' : ' {2}',
indentB = isDeep ? ' {6,8}' : ' {6}',
result = [];
var patterns = [
// Match varaibles at the start of a declaration list.
['^(' + indentA + 'var\\s+)([$\\w]+)\\s*=.+?,\\n *', 2, 1],
// Match variable declarations in a declaration list.
[',\\n' + consts.rsComment + indentB + '([$\\w]+)\\s*=[\\s\\S]+?(?=[,;]\\n)', 1, -1],
// Match variables that aren't part of a declaration list.
['^(' + indentA + ')var\\s+([$\\w]+)\\s*(?:|=[^;]+);\\n', 2, -1]
];
source = removeStrings(removeComments(source));
_.each(listing.complexVars, function(varName) {
source = modifyVar(source, varName, function() {
result.push(varName);
return '';
});
});
return _.uniq(_.transform(patterns, function(result, pattern) {
source = source.replace(RegExp(pattern[0], 'gm'), function() {
result.push(arguments[pattern[1]]);
return pattern[2] > -1 ? arguments[pattern[2]] : '';
});
}, result));
}
/**
* Checks if `source` is a function snippet.
*
* @private
* @param {string} source The source to inspect.
* @returns {boolean} Returns `true` for a function snippet, else `false`.
*/
function isFunctionSnippet(source) {
var header = getHeader(source);
return consts.reHasFuncTags.test(header) || consts.reIsFuncSnippet.test(replace(source, header, ''));
}
/**
* Checks if `identifier` is private.
*
* @private
* @param {string} identifier The identifier to query.
* @returns {boolean} Returns `true` if the identifier is private, else `false`.
*/
function isPrivate(identifier) {
identifier = getRealName(identifier);
if (_.includes(listing.categories, identifier)) {
return false;
}
return _.isUndefined(_.findKey(mapping.category, function(identifiers) {
return _.includes(identifiers, identifier);
}));
}
/**
* Checks if the variable `varName` is used in `source`.
*
* @private
* @param {string} source The source to process.
* @param {string} varName The name of the variable.
* @returns {boolean} Returns `true` if the variable is used, else `false`.
*/
function isVarUsed(source, varName) {
var escapedName = _.escapeRegExp(varName);
source = removeVar(source, varName);
return RegExp('[^.$"\'\\w]' + escapedName + '\\b(?!\\s*=)').test(source);
}
/**
* Searches `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.
* @param {boolean} [leadingComments] A flag to specify including leading comments.
* @returns {string} Returns the matched function snippet.
*/
var matchFunction = _.memoize(function(source, funcName, leadingComments) {
var escapedName = _.escapeRegExp(funcName),
otherKey = funcName + ':' + !leadingComments;
var patterns = [
// Match function declarations.
['^(' + consts.rsComment + ')(( *)function\\s+' + escapedName + '\\((?:\\)\\s*\\{\\s*|[\\s\\S]+?\\n\\3)\\}\\n)', 2, 0],
// Match single line function expressions at the start of a declaration list.
['^( *var\\s+)(' + escapedName + '\\s*=\\s*\\(?function\\b.+?\\}\\)?,\\n *)', 2, 2],
// Match single line function expressions in a declaration list.
['(,\\n' + consts.rsComment + ')( *' + escapedName + '\\s*=\\s*\\(?function\\b.+?\\}\\)?(?=[,;]\\n))', 2, 0],
// Match single line function expressions that aren't in a declaration list.
['^(' + consts.rsComment + ')( *var\\s+' + escapedName + '\\s*=\\s*\\(?function\\b.+?\\}\\)?;\\n)', 2, 0],
// Match variable declarations containing built-in constructors.
['^( *var\\s+)(' + escapedName + '\\s*=\\s*root\\.(?:[A-Z][a-z0-9]+)+,\\n *)', 2, 2],
['(,\\n' + consts.rsComment + ')( *' + escapedName + '\\s*=\\s*root\\.(?:[A-Z][a-z0-9]+)+(?=[,;]\\n))', 2, 0],
['^(' + consts.rsComment + ')( *var\\s+' + escapedName + '\\s*=\\s*root\\.(?:[A-Z][a-z0-9]+)+;\\n)', 2, 0],
// Match variable declarations using `baseProperty` or `basePropertyOf`.
['(,\\n' + consts.rsComment + ')( *' + escapedName + '\\s*=\\s*baseProperty(?:Of)?\\([\\s\\S]+?\\)(?=[,;]\\n))', 2, 0],
['^(' + consts.rsComment + ')( *var\\s+' + escapedName + '\\s*=\\s*baseProperty(?:Of)?\\([\\s\\S]+?\\);\\n)', 2, 0],
// Match variable declarations using creator functions.
['(,\\n' + consts.rsComment + ')(( *)' + escapedName + '\\s*=\\s*create(?:[A-Z][a-z]+)+\\((?:.*|[\\s\\S]+?\\n\\3(?:\\S.*?)?)\\)(?=[,;]\\n))', 2, 0],
['^(' + consts.rsComment + ')(( *)var\\s+' + escapedName + '\\s*=\\s*create(?:[A-Z][a-z]+)+\\((?:.*|[\\s\\S]+?\\n\\3(?:\\S.*?)?)\\);\\n)', 2, 0],
// Match variable declarations using `getNative`.
['^( *var\\s+)(' + escapedName + '\\s*=\\s*getNative\\(.+?\\),\\n *)', 2, 2],
['(,\\n' + consts.rsComment + ')( *' + escapedName + '\\s*=\\s*getNative\\(.+?\\)(?=[,;]\\n))', 2, 0],
['^(' + consts.rsComment + ')( *var\\s+' + escapedName + '\\s*=\\s*getNative\\(.+?\\);\\n)', 2, 0],
// Match variable declarations using `overArg`.
['(,\\n' + consts.rsComment + ')( *' + escapedName + '\\s*=\\s*overArg\\([\\s\\S]+?\\)(?=[,;]\\n))', 2, 0],
['^(' + consts.rsComment + ')( *var\\s+' + escapedName + '\\s*=\\s*overArg\\([\\s\\S]+?\\);\\n)', 2, 0],
// Match variable declarations using `shortOut`.
['(,\\n' + consts.rsComment + ')( *' + escapedName + '\\s*=\\s*shortOut\\([\\s\\S]+?\\)(?=[,;]\\n))', 2, 0],
['^(' + consts.rsComment + ')( *var\\s+' + escapedName + '\\s*=\\s*shortOut\\([\\s\\S]+?\\);\\n)', 2, 0],
// Match variable declarations using `_.memoize` or `_.rest`.
['(,\\n' + consts.rsComment + ')(( *)' + escapedName + '\\s*=\\s*(?:[a-z]+Rest|memoize(?:Capped)?)\\((?:.+|[\\s\\S]+?\\n\\3\\})\\)(?=[,;]\\n))', 2, 0],
['^(' + consts.rsComment + ')(( *)var\\s+' + escapedName + '\\s*=\\s*(?:[a-z]+Rest|memoize(?:Capped)?)\\((?:.+|[\\s\\S]+?\\n\\3\\})\\);\\n)', 2, 0],
// Match simple variable declaration.
['^(' + consts.rsComment + ')( *var\\s+' + escapedName + '\\s*=.+?;\\n)', 2, 0],
// Match multiple line function expressions.
['^(' + consts.rsComment + ')(( *)var\\s+' + escapedName + '\\s*=.*?function\\b[\\s\\S]+?\\{\\n[\\s\\S]+?\\n\\3\\}(?:(?:\\(\\))?\\))?;\\n)', 2, 0]
];
var result = _.reduce(patterns, function(result, pattern) {
if (!result) {
result = RegExp(pattern[0], 'm').exec(source) || '';
if (isFunctionSnippet(_.get(result, 0))) {
matchFunction.cache.set(otherKey, result[pattern[leadingComments ? 1 : 2]]);
result = result[pattern[leadingComments ? 2 : 1]];
}
}
return result;
}, '');
if (!result) {
matchFunction.cache.set(otherKey, result);
}
return result;
}, function(source, funcName, leadingComments) {
return funcName + ':' + !!leadingComments;
});
/**
* Searches `source` for a lodash property, of the given property name, and
* returns the matched snippet.
*
* @private
* @param {string} source The source to inspect.
* @param {string} propName The name of the property to match.
* @param {boolean} [leadingComments] A flag to specify including leading comments.
* @returns {string} Returns the matched property snippet.
*/
function matchProp(source, propName, leadingComments) {
var escapedName = _.escapeRegExp(propName);
return _.get(RegExp(
'^' + (leadingComments ? consts.rsComment : '') +
'(?: {2,4}var\\s+' + escapedName + '\\b.+|(?:\\s*|.*?=\\s*)lodash\\._?' + escapedName + '\\s*)=[\\s\\S]+?' +
'(?:\\(function\\([\\s\\S]+?\\}\\([^)]*\\)\\);\\n(?=\\n)|' +
'[;}]\\n(?=\\n(?!\\s*\\(function\\b)))'
, 'm').exec(source), 0, '');
}
/**
* Searches `source` for a `varName` variable assignment and returns
* the matched snippet.
*
* @private
* @param {string} source The source to inspect.
* @param {string} varName The name of the variable to match.
* @param {boolean} [leadingComments] A flag to specify including leading comments.
* @returns {string} Returns the matched variable snippet.
*/
function matchVar(source, varName, leadingComments) {
var escapedName = _.escapeRegExp(varName);
var patterns = [
// Match varaibles at the start of a declaration list.
['^( *var\\s+)(' + escapedName + '\\s*=.+?,\\n *)', 2, 2],
// Match variables in a declaration list.
['(,\\n' + consts.rsComment + ')( *' + escapedName + '\\s*=[\\s\\S]+?(?=[,;]\\n))', 2, 0],
// Match variable declarations that aren't in a declaration list.
['^(' + consts.rsComment + ')( *var\\s+' + escapedName + '\\s*(?:|=[^;]+);\\n)', 2, 0]
];
// Match complex variable assignments.
if (_.includes(listing.complexVars, varName)) {
patterns.splice(2, 0, ['^(' + consts.rsComment + ')(( *)var\\s+' + escapedName + '\\s*=[\\s\\S]+?[};]\\n(?=\\s*\\n(?:\\S|\\3(?:function\\b|if\\b|lodash\\b|var\\s|/[/*]))))', 2, 0]);
}
return _.reduce(patterns, function(result, pattern) {
return result || _.get(RegExp(pattern[0], 'm').exec(source), pattern[leadingComments ? 2 : 1], '');
}, '');
}
/**
* Modifies the `funcName` function of `source`.
*
* @private
* @param {string} source The source to modify.
* @param {string} funcName The name of the function to match.
* @param {Function} replacer The function to modify the matched source.
* @returns {string} Returns the modified source.
*/
function modifyFunction(source, funcName, replacer) {
return replace(source, matchFunction(source, funcName), function(match) {
var result = replacer(match);
matchFunction.cache.set(funcName + ':false', result);
matchFunction.cache.delete(funcName + ':true');
return result;
});
}
/**
* Modifies the `propName` lodash property of `source`.
*
* @private
* @param {string} source The source to modify.
* @param {string} propName The name of the property to match.
* @param {Function} replacer The function to modify the matched source.
* @returns {string} Returns the modified source.
*/
function modifyProp(source, propName, replacer) {
return replace(source, matchProp(source, propName), _.unary(replacer));
}
/**
* Modifies the `varName` variable of `source`.
*
* @private
* @param {string} source The source to modify.
* @param {string} varName The name of the variable to match.
* @param {Function} replacer The function to modify the matched source.
* @returns {string} Returns the modified source.
*/
function modifyVar(source, varName, replacer) {
return replace(source, matchVar(source, varName), _.unary(replacer));
}
/**
* Converts a comma separated option value into an array.
*
* @private
* @param {string} name The name of the option to inspect.
* @param {string} string The options string.
* @returns {Array} Returns the new converted array.
*/
function optionToArray(name, string) {
return _.compact(_.invokeMap((optionToValue(name, string) || '').split(/, */), 'trim'));
}
/**
* Extracts the option value from an option string.
*
* @private
* @param {string} name The name of the option to inspect.
* @param {string} string The options string.
* @returns {string|undefined} Returns the option value, else `undefined`.
*/
function optionToValue(name, string) {
var result = RegExp('^' + _.escapeRegExp(name) + '(?:=([\\s\\S]+))?$').exec(string);
if (result) {
result = _.get(result, 1);
result = result ? _.trim(result) : true;
}
return (result !== 'false') && (result || undefined);
}
/**
* Removes all lodash method and property assignments from `source`.
*
* @private
* @param {string} source The source to process.
* @returns {string} Returns the modified source.
*/
function removeAssignments(source) {
// Remove method and intermediate assignments.
source = removeMethodAssignments(source);
return source.replace(/(=\s*)lodash\.[$\w]+\s*=\s*/g, '$1');
}
/**
* Removes support for lodash wrapper chaining in `source`.
*
* @private
* @param {string} source The source to process.
* @param {Object} [seqDepMap] The chain dependency map to modify.
* @param {Object} [funcDepMap] The function dependency map to modify.
* @param {Object} [varDepMap] The variable dependency map to modify.
* @returns {string} Returns the modified source.
*/
function removeChaining(source, seqDepMap, funcDepMap, varDepMap) {
source = removeLazyChaining(source, seqDepMap, funcDepMap, varDepMap);
source = removeMixinCalls(source);
// Remove all `lodash.prototype` additions.
return source
.replace(/^(?: *\/\/.*\n)*( *)[$\w]+\(\['pop'[\s\S]+?\n\1\}\);\n/m, '')
.replace(/^(?: *\/\/.*\n)*( *)if\s*\(symIterator\)[\s\S]+?\n\1\}\n/gm, '')
.replace(/^(?: *\/\/.*\n)*( *)lodash\.prototype\.(?!constructor\b)[$\w]+\s*=[\s\S]+?;\n/gm, '');
}
/**
* Removes all comments from `source`.
*
* @private
* @param {string} source The source to process.
* @returns {string} Returns the modified source.
*/
function removeComments(source) {
return replace(source, consts.reComment, '');
}
/**
* Removes the `getTag` fork from `source`.
*
* @private
* @param {string} source The source to process.
* @returns {string} Returns the modified source.
*/
function removeGetTagFork(source) {
return replace(source, getGetTagFork(source), '');
}
/**
* Removes support for lazy chaining in `source`.
*
* @private
* @param {string} source The source to process.
* @param {Object} [seqDepMap] The chain dependency map to modify.
* @param {Object} [funcDepMap] The function dependency map to modify.
* @param {Object} [varDepMap] The variable dependency map to modify.
* @returns {string} Returns the modified source.
*/
function removeLazyChaining(source, seqDepMap, funcDepMap, varDepMap) {
if (seqDepMap) {
_.pull(seqDepMap.varDep, 'realNames');
_.pull(seqDepMap.funcDep,
'baseInvoke', 'baseRest', 'createHybrid', 'getIteratee', 'identity',
'isArray', 'last', 'lazyClone', 'lazyReverse', 'lazyValue', 'LazyWrapper',
'toInteger', 'wrapperAt', 'wrapperReverse'
);
}
if (funcDepMap) {
funcDepMap.createFlow = ['baseFlatten', 'flatRest'];
}
if (varDepMap) {
source = removeVar(source, 'realNames');
_.forOwn(varDepMap, function(depNames, identifier) {
_.pull(depNames, 'realNames');
});
}
source = replaceFunction(source, 'createFlow', [
'function createFlow(fromRight) {',
' return flatRest(function(funcs) {',
' funcs = baseFlatten(funcs, 1);',
'',
' var length = funcs.length,',
' index = length;',
'',
' if (fromRight) {',
' funcs.reverse();',
' }',
' while (index--) {',
" if (typeof funcs[index] != 'function') {",
' throw new TypeError(FUNC_ERROR_TEXT);',
' }',
' }',
' return function() {',
' var index = 0,',
' result = length ? funcs[index].apply(this, arguments) : arguments[0];',
'',
' while (++index < length) {',
' result = funcs[index].call(this, result);',
' }',
' return result;',
' };',
' });',
'}'
].join('\n'));
return source
// Remove bulk `LazyWrapper.prototype` assignments.
.replace(/^(?: *\/\/.*\n)*( *)arrayEach\(\['(?:drop|filter|head|initial)\b[\s\S]+?\n\1\}\);\n/gm, '')
// Remove individual `LazyWrapper` method assignments.
.replace(/^(?: *\/\/.*\n)*( *)LazyWrapper\.prototype\.(?:compact|find|findLast|invokeMap|reject|slice|takeRightWhile|toArray)\s*=[\s\S]+?\n\1\}.*?;\n/gm, '')
// Remove other `LazyWrapper.prototype` assignments.
.replace(/^(?: *\/\/.*\n)* *LazyWrapper\.prototype\.(?!constructor\b)[$\w]+\s*=[^;]+;\n/gm, '')
// Remove `LazyWrapper` additions to `LodashWrapper` and `realNames` assignments.
.replace(/^(?: *\/\/.*\n)*( *)baseForOwn\(LazyWrapper\.prototype\b[\s\S]+?\n\1\}\);\n/gm, '')
// Remove `realNames` assignment for `wrapper`.
.replace(/^(?: *\/\/.*\n)* *realNames\[createHybrid\b[\s\S]+?;\n/m, '')
// Remove lazy alias `lodash.prototype` assignments.
.replace(/^(?: *\/\/.*\n)* *lodash\.prototype\.first\s*=[^;]+;\n/gm, '');
}
/**
* Removes metadata optimizations from `source`.
*
* @private
* @param {string} source The source to process.
* @param {Object} [funcDepMap] The function dependency map to modify.
* @returns {string} Returns the modified source.
*/
function removeMetadata(source, funcDepMap) {
var deps = _.get(funcDepMap, 'createWrap', []);
_.pull(deps, 'baseSetData', 'getData', 'mergeData', 'setData');
deps = _.get(funcDepMap, 'createRecurry', []);
_.pull(deps, 'isLaziable', 'setData');
// Remove metadata related code.
source = modifyFunction(source, 'createWrap', function(match) {
match = _.reduce(['data', 'funcBitmask', 'funcIsPartialed', 'setter'], removeVar, match);
return match
.replace(/^(?: *\/\/.*\n)*( *)if\s*\((?:typeof\s+)?data\b[\s\S]+?\n\1\}\n/gm, '')
.replace(/\bsetter\(([^,]+),[^)]+\)/, '$1');
});
source = modifyFunction(source, 'createRecurry', function(match) {
match = removeVar(match, 'newData');
match = replaceVar(match, 'result', 'wrapFunc(func, bitmask, thisArg, newPartials, newHolders, newPartialsRight, newHoldersRight, argPos, ary, arity)');
return match.replace(/^(?: *\/\/.*\n)*( *)if\s*\(isLaziable\b[\s\S]+?\n\1\}\n/m, '');
});
return source;
}
/**
* Removes the `funcName` function declaration, expression, or assignment and
* associated code from `source`.
*
* @private
* @param {string} source The source to process.
* @param {string} funcName The name of the function to remove.
* @returns {string} Returns the modified source.
*/
function removeFunction(source, funcName) {
source = toString(source);
source = funcName == 'runInContext'
? removeRunInContext(source, funcName)
: replace(source, matchFunction(source, funcName, true), '');
matchFunction.cache.set(funcName + ':true', '');
matchFunction.cache.set(funcName + ':false', '');
return source;
}
/**
* Removes all references to `getIteratee` from `source` and replaces calls
* with `baseIteratee`.
*
* @private
* @param {string} source The source to process.
* @param {Object} [funcDepMap] The function dependency map to modify.
* @returns {string} Returns the modified source.
*/
function removeGetIteratee(source, funcDepMap) {
source = removeFunction(source, 'getIteratee');
_.forOwn(funcDepMap, function(deps, identifier) {
if (_.includes(deps, 'getIteratee')) {
_.pull(deps, 'getIteratee');
if (!_.includes(deps, 'baseIteratee')) {
deps.push('baseIteratee');
}
source = modifyFunction(source, identifier, function(match) {
return match.replace(consts.reGetIteratee, 'baseIteratee');
});
}
});
return source.replace(getMethodAssignments(source), function(match) {
var deps = _.get(funcDepMap, 'main', []);
if (_.includes(deps, 'baseIteratee')) {
match = match.replace(consts.reGetIteratee, 'baseIteratee');
}
return match;
});
}
/**
* Removes the copyright header from `source`.
*
* @private
* @param {string} source The source to process.
* @returns {string} Returns the modified source.
*/
function removeHeader(source) {
return replace(source, getHeader(source), '');
}
/**
* Removes the `@license` tag from the copyright header so minifiers
* and build optimizers may strip them.
*
* @private
* @param {string} source The source to inspect.
* @returns {string} Returns the modified source.
*/
function removeLicenseTag(source) {
return replace(source, /^ \* *@license\n/m, '');
}
/**
* Removes a method assignment by name from `source`.
*
* @private
* @param {string} source The source to process.
* @param {string} [methodName] The name of the method assignment to remove.
* @returns {string} Returns the modified source.
*/
function removeMethodAssignment(source, methodName) {
return replace(source, getMethodAssignments(source), function(match) {
var pattern = RegExp(
'^( *//.*\\n)*( *)' +
'(?:lodash(?:\\.prototype)?\\.[$\\w]+\\s*=\\s*)*' +
'lodash(?:\\.prototype)?\\.' +
'(?:[$\\w]+\\s*=\\s*' + methodName + '|' + methodName + '\\s*=\\s*(?:[$\\w]+|function\\b[\\s\\S]+?\\n\\2\\}));' +
'\\n(\\n)?'
, 'gm');
return match.replace(pattern, function(match, comment, indent, newline) {
return newline || comment || '';
});
});
}
/**
* Removes all lodash method assignments from `source`.
*
* @private
* @param {string} source The source to process.
* @returns {string} Returns the modified source.
*/
function removeMethodAssignments(source) {
return replace(source, getMethodAssignments(source), '');
}
/**
* Removes all `_.mixin` calls from `source`.
*
* @private
* @param {string} source The source to inspect.
* @returns {string} Returns the modified source.
*/
function removeMixinCalls(source) {
return replace(source, /^(?: *\/\/.*\n)*( *)mixin\(.+?(?:\{[\s\S]+?\n\1\}.+?)?\);\n/gm, '');
}
/**
* Removes a lodash property, of the given property name, from `source`.
*
* @private
* @param {string} source The source to process.
* @param {string} propName The name of the property to remove.
* @returns {string} Returns the modified source.
*/
function removeProp(source, propName) {
return replace(source, matchProp(source, propName, true), '');
}
/**
* Removes all `runInContext` references from `source`.
*
* @private
* @param {string} source The source to process.
* @returns {string} Returns the modified source.
*/
function removeRunInContext(source) {
source = removeFunction(source, 'clearTimeout');
source = removeFunction(source, 'setTimeout');
source = removeMethodAssignment(source, 'runInContext');
source = removeVar(source, 'contextProps');
// Remove function scaffolding, leaving most of its content.
source = replace(source, matchFunction(source, 'runInContext', true), function(match) {
// Remove function frame.
match = match.replace(/^[\s\S]+?function\s+runInContext\b[\s\S]+?context\s*=\s*context.+?\n+| *return\s+lodash;[\s\S]+$/g, '');
match = replaceIndent(match, 1, 2);
// Remove built-in vars and related `context` references.
_.each(listing.builtins, function(builtin) {
match = removeVar(match, builtin);
match = match.replace(RegExp('\\bcontext\\.(?=' + builtin + '\\b)', 'g'), '');
});
// Remove `ctx` vars.
_.each(['ctxClearTimeout', 'ctxNow', 'ctxSetTimeout'], function(varName) {
match = removeVar(match, varName);
match = match.replace(RegExp('\\b' + varName + '\\s*\\|\\|\\s*'), '');
});
// Replace remaining `context` references with `root`.
return match.replace(/\bcontext\b/g, 'root');
});
return source
// Remove `_` assignment.
.replace(/^(?: *\/\/.*\n)* *var\s+_\s*=\s*runInContext\b.+\n+/m, '')
// Replace `_` references with `lodash`.
.replace(/(\breturn\s+|=\s*)_([;)])/g, '$1lodash$2');
}
/**
* Removes all strings from `source`.
*
* @private
* @param {string} source The source to process.
* @returns {string} Returns the modified source.
*/
function removeStrings(source) {
return replace(source, consts.reString, '');
}
/**
* Removes a variable of the given variable name from `source`.
*
* @private
* @param {string} source The source to process.
* @param {string} varName The name of the variable to remove.
* @returns {string} Returns the modified source.
*/
function removeVar(source, varName) {
return replace(source, matchVar(source, varName, true), '');
}
/**
* Replaces `pattern` matches in `string` with the resolved `replacement` value.
*
* @private
* @param {string} string The string to modify.
* @param {RegExp|string} pattern The pattern to match.
* @param {Function|string} replacement The replacement value.
* @returns {string} Returns the modified string.
*/
function replace(string, pattern, replacement) {
string = toString(string);
return pattern ? string.replace(pattern, replacement) : string;
}
/**
* Replaces the `funcName` function body in `source` with `funcValue`.
*
* @private
* @param {string} source The source to process.
* @param {string} funcName The name of the function to replace.
* @param {string} funcValue The replacement value.
* @returns {string} Returns the modified source.
*/
function replaceFunction(source, funcName, funcValue) {
var leadingComments = consts.reIsVarSnippet.test(funcValue);
return replace(source, matchFunction(source, funcName, leadingComments), function(match) {
var header = leadingComments ? getHeader(match) : '';
if (!isFunctionSnippet(match)) {
header = header.replace(/^( *)\* *(?:@(?:category|param|returns)\b|\/)/m, function(match, indent) {
return indent + '* @type +\{Function\}\n' + match;
});
}
funcValue = funcValue
.replace(RegExp('^' + getIndent(funcValue), 'gm'), getIndent(match))
.trimRight() + '\n';
if (leadingComments) {
matchFunction.cache.set(funcName + ':false', funcValue);
} else {
matchFunction.cache.delete(funcName + ':true');
}
var result = header + funcValue;
matchFunction.cache.set(funcName + ':' + leadingComments, result);
return result;
});
}
/**
* Replaces the IIFE that wraps `source` with `iife`. If the `%output%` token
* is present in `iife` it will be replaced with the unwrapped `source`.
*
* @private
* @param {string} source The source to process.
* @param {string} iife The replacement IIFE.
* @returns {string} Returns the modified source.
*/
function replaceIIFE(source, iife) {
source = toString(source);
iife = toString(iife);
var token = '%output%',
header = getHeader(source),
index = iife.indexOf(token);
if (index < 0) {
return header + iife;
}
return header +
iife.slice(0, index) +
source.replace(/^[\s\S]+?\(function[^{]+\{\n+|\s*\}\.call\(this\)\)[;\s]*$/g, '\n') +
iife.slice(index + token.length);
}
/**
* Replaces the indent at level `from` of the given source with the level `to`.
*
* @private
* @param {string} source The source to process.
* @param {number} [to=0] The indent level to replace with.
* @param {number} [from=1] The indent level to be replaced.
* @returns {string} Returns the modified source.
*/
function replaceIndent(source, to, from) {
source = toString(source);
if (from === undefined) {
from = floor(getIndent(source).length / 2);
}
return source.replace(RegExp('^(?: ){' + (from || 1) + '}', 'gm'), _.repeat(' ', to || 0));
}
/**
* Replaces the `varName` variable declaration value in `source` with `varValue`.
*
* @private
* @param {string} source The source to inspect.
* @param {string} varName The name of the variable to replace.
* @param {string} varValue The replacement value.
* @returns {string} Returns the modified source.
*/
function replaceVar(source, varName, varValue) {
source = toString(source);
var escapedName = _.escapeRegExp(varName),
modified = false;
// Replace a variable that's not part of a declaration list.
source = source.replace(RegExp(
'(( *)var\\s+' + escapedName + '\\s*=)' +
'(?:.+?;|(?:Function\\(.+?|.*?[^,])\\n[\\s\\S]+?\\n\\2.+?;)\\n'
), function(match, left) {
modified = true;
return left + ' ' + varValue + ';\n';
});
if (!modified) {
// Replace a varaible at the start or middle of a declaration list.
source = source.replace(RegExp('((?:var|\\n)\\s+' + escapedName + '\\s*=).+?(?=,\\n)'), function(match, left) {
modified = true;
return left + ' ' + varValue;
});
}
if (!modified) {
// Replace a variable at the end of a variable declaration list.
source = source.replace(RegExp('(,\\s*' + escapedName + '\\s*=).+?(?=;\\n)'), function(match, left) {
return left + ' ' + varValue;
});
}
return source;
}
/**
* Add or remove the "use strict" directive from `source`.
*
* @private
* @param {string} source The source to process.
* @param {boolean} value The value to set.
* @returns {string} Returns the modified source.
*/
function setUseStrictOption(source, value) {
return replace(source, /^([\s\S]*?function[^{]+\{)(?:\s*'use strict';)?/, '$1' + (value ? "\n 'use strict';" : ''));
}
/**
* Invokes `callback` providing `source` with strings removed and returns the
* modified source with strings restored.
*
* @private
* @param {string} source The source to modify.
* @param {Function} [callback] The function to modify the string free source.
* @returns {string} Returns the modified source.
*/
function stringFree(source, callback) {
var strings = [];
source = callback(replace(source, consts.reString, function(match) {
var index = strings.length;
strings.push(match);
return '<#str_token' + index + '#>';
})) || '';
return replace(source, consts.reStringToken, function(match) {
return strings[match.slice(11, -2)];
});
}
/**
* Converts `value` to a string if it's not one.
* An empty string is returned for `null` and `undefined` values.
*
* @private
* @param {*} value The value to process.
* @returns {string} Returns the string.
*/
function toString(value) {
if (typeof value == 'string') {
return value;
}
return value == null ? '' : (value + '');
}
/*----------------------------------------------------------------------------*/
/**
* Creates a development and/or production build invoking `callback` for each.
* The `callback` is invoked with one argument: (data).
*
* **Note:** For a list of commands see `consts.helpText` or run `lodash --help`.
*
* @param {Array|Object} [options=[]] An array of build commands or the state object.
* @param {Function} [callback=defaultBuildCallback] The function called per build.
*/
function build(options, callback) {
options || (options = []);
callback || (callback = defaultBuildCallback);
// Used to specify the output path for builds.
var outputPath;
// Used to specify the source map URL.
var sourceMapURL;
// Used to track the time it takes to complete a build.
var stamp = _.now();
// Used to pre-populate the build state.
var state = _.isPlainObject(options) && options;
var isExcluded = function() {
return _.every(arguments, function(value) {
return !_.includes(buildFuncs, value);
});
};
if (state) {
var buildFuncs = state.buildFuncs,
filePath = state.filePath,
funcDepMap = state.funcDepMap,
funcTokenMap = state.funcTokenMap,
includeFuncs = state.includeFuncs,
includeVars = state.includeVars,
isDevelopment = true,
isModularize = true,
isStdOut = state.isStdOut,
isStrict = state.isStrict,
minusFuncs = [],
outputPath = state.outputPath,
plusFuncs = [],
source = state.source,
varDepMap = state.varDepMap,
varNames = state.varNames;
}
else {
// Clear `matchFunction` memoize cache.
matchFunction.cache.clear();
// Clone dependencies to modify.
var seqDepMap = new Hash(_.cloneDeep(mapping.seqDep)),
funcDepMap = new Hash(_.cloneDeep(mapping.funcDep)),
varDepMap = new Hash(_.cloneDeep(mapping.varDep));
// The path to the source file.
var filePath = require.resolve('lodash');
// Used to specify a custom IIFE to wrap lodash.
var iife = getOption(options, 'iife');
// Used to match external template files to precompile.
var templatePattern = getOption(options, 'template', '');
// Used as the template settings for precompiled templates.
var templateSettings = (function() {
var result = getOption(options, 'settings');
return result
? Function('return {' + result.replace(/^\{|\}$/g, '') + '}')()
: _.clone(_.templateSettings);
}());
// A flag to specify a core build.
var isCore = getOption(options, 'core');
// A flag to specify only creating the development build.
var isDevelopment = getOption(options, ['-d', '--development']);
// A flag to indicate that a custom IIFE was specified.
var isIIFE = typeof iife == 'string';
// A flag to specify creating a source map for the minified source.
var isMapped = getOption(options, ['-m', '--source-map']);
// A flag to specify a modularize build.
var isModularize = getOption(options, 'modularize');
// A flag to specify only creating the minified build.
var isProduction = getOption(options, ['-p', '--production']);
// A flag to specify writing output to standard output.
var isStdOut = getOption(options, ['-c', '--stdout']);
// A flag to specify skipping status updates normally logged to the console.
var isSilent = !isBin || isStdOut || getOption(options, ['-s', '--silent']);
// A flag to specify `_.assign`, `_.bindAll`, and `_.defaults`
// are constructed using the "use strict" directive.
var isStrict = getOption(options, 'strict');
// A flag to specify a template build.
var isTemplate = !!templatePattern;
// Used to specify the AMD module ID of lodash used by precompiled templates.
var moduleId = getOption(options, 'moduleId');
// Used as the output path for the build.
var outputPath = _.reduce(options, function(result, value, index) {
return /^(?:-o|--output)$/.test(value)
? path.normalize(options[index + 1])
: result;
}, isModularize ? ('.' + path.sep + 'modularize') : '');
// Used to specify the ways to export the `lodash` function.
var exportsOptions = (function() {
var result = getOption(options, 'exports', buildExports.defaults[isModularize ? 'modularize' : 'monolithic']);
if (!isModularize && _.includes(result, 'umd')) {
result = _.union(result, buildExports.umd);
}
return isModularize ? _.take(result, 1) : result;
}());
// A flag to specify creating a custom build.
var isCustom = !isModularize && (
isCore || isMapped || isStrict || isTemplate ||
/\b(?:category|exports|iife|include|minus|moduleId|plus)=/.test(options) ||
!_.isEqual(exportsOptions, buildExports.defaults.monolithic)
);
// Flags to specify export options.
var isAMD = _.includes(exportsOptions, 'amd'),
isES = _.includes(exportsOptions, 'es'),
isGlobal = _.includes(exportsOptions, 'global'),
isNpm = _.includes(exportsOptions, 'npm'),
isNode = isNpm || _.includes(exportsOptions, 'node');
if (isTemplate) {
isModularize = false;
}
// The lodash.js source.
var source = fs.readFileSync(filePath, 'utf8');
/*------------------------------------------------------------------------*/
// Categories of functions to include in the build.
var categoryOptions = _.map(getOption(options, 'category', []), function(category) {
return getRealCategory(_.capitalize(category.toLowerCase()));
});
// Functions to include in the build.
var includeFuncs = _.union(categoryOptions, _.map(getOption(options, 'include', []), getRealName));
// Variables to include in the build.
var includeVars = _.intersection(includeFuncs, listing.varDeps);
// Functions to remove from the build.
var minusFuncs = _.map(getOption(options, 'minus', []), getRealName);
// Functions to add to the build.
var plusFuncs = _.map(getOption(options, 'plus', []), getRealName);
// Expand categories to function names.
_.each([includeFuncs, minusFuncs, plusFuncs], function(funcNames) {
var categories = _.intersection(funcNames, listing.categories);
_.each(categories, function(category) {
push.apply(funcNames, _.filter(getNamesByCategory(category), function(key) {
var type = typeof _[key];
return type == 'function' || type == 'undefined';
}));
});
});
// Remove categories from function names.
includeFuncs = _.difference(includeFuncs, listing.categories, includeVars);
minusFuncs = _.difference(minusFuncs, listing.categories);
plusFuncs = _.difference(plusFuncs, listing.categories);
/*------------------------------------------------------------------------*/
// Used to capture warnings for invalid command-line arguments.
var warnings = [];
// Used to detect invalid command-line arguments.
var invalidArgs = _.reject(options, function(value, index, options) {
if (/^(?:-o|--output)$/.test(options[index - 1]) ||
/^(?:category|exports|iife|include|minus|moduleId|plus|settings|template)=[\s\S]*$/.test(value)) {
return true;
}
var result = _.includes(listing.buildFlags, value);
if (!result && /^(?:-m|--source-map)$/.test(options[index - 1])) {
sourceMapURL = value;
return true;
}
return result;
});
// Report invalid command and option arguments.
if (!_.isEmpty(invalidArgs)) {
warnings.push('Invalid argument' + (_.size(invalidArgs) > 1 ? 's' : '') + ' passed: ' + invalidArgs.join(', '));
}
// Report invalid command entries.
_.forOwn({
'category': {
'entries': categoryOptions,
'validEntries': listing.categories
},
'exports': {
'entries': exportsOptions,
'validEntries': buildExports[isModularize ? 'modularize' : 'monolithic']
},
'include': {
'entries': includeFuncs,
'validEntries': listing.funcs
},
'minus': {
'entries': minusFuncs,
'validEntries': listing.funcs
},
'plus': {
'entries': plusFuncs,
'validEntries': listing.funcs
}
}, function(data, commandName) {
invalidArgs = _.difference(data.entries, data.validEntries, ['none']);
if (!_.isEmpty(invalidArgs)) {
warnings.push('Invalid `' + commandName + '` entr' + (_.size(invalidArgs) > 1 ? 'ies' : 'y') + ' passed: ' + invalidArgs.join(', '));
}
});
if (!_.isEmpty(warnings)) {
var warnText = [
'',
warnings,
'For more information type: lodash --help'
].join('\n');
if (isBin) {
console.warn(warnText);
process.exit(1);
} else {
callback(_.create(Error.prototype, { 'message': warnText, 'source': warnText }));
}
return;
}
// Display the help message.
if (getOption(options, ['-h', '--help'])) {
var helpText = consts.helpText;
if (isBin) {
console.log(helpText);
} else {
callback({ 'source': helpText });
}
return;
}
// Display the `lodash.VERSION`.
if (getOption(options, ['-V', '--version'])) {
if (isBin) {
console.log(_.VERSION);
} else {
callback({ 'source': _.VERSION });
}
return;
}
/*------------------------------------------------------------------------*/
// The names of functions to include in the build.
var buildFuncs = !isTemplate && (function() {
source = setUseStrictOption(source, isStrict);
if (isModularize) {
// Remove `clearTimeout` and `setTimeout` deps.
_.forOwn(funcDepMap, function(depNames) {
_.pull(depNames, 'clearTimeout', 'setTimeout');
});
// Remove `lodash.placeholder` references.
_.pull(funcDepMap.getHolder, 'lodash');
source = modifyFunction(source, 'getHolder', function(match) {
return replaceVar(match, 'object', 'func');
});
// Add deps to wrap `_.mixin` in `main`.
funcDepMap.main.push('baseFunctions', 'isObject', 'keys', 'mixin');
}
else {
// Add `arrayEach` to functions with placeholder support because it's
// used to reduce code for placeholder assignments
_.each(listing.placeholderFuncs, function(funcName) {
funcDepMap[funcName].push('arrayEach');
});
}
if (isModularize) {
if (isNpm) {
source = removeChaining(source, seqDepMap, funcDepMap, varDepMap);
source = removeMetadata(source, funcDepMap);
}
// Remove `getIteratee` use from iteration methods.
_.each(['forEach', 'forEachRight', 'forIn', 'forInRight', 'forOwn', 'forOwnRight', 'times'], function(funcName) {
_.pull(funcDepMap[funcName], 'getIteratee').push('castFunction');
source = modifyFunction(source, funcName, function(match) {
return match.replace(/\bgetIteratee\(([^,\)]+)[\s\S]*?\)/, 'castFunction($1)');
});
});
}
if (isCore && _.isEmpty(plusFuncs)) {
source = removeLazyChaining(source, seqDepMap, funcDepMap, varDepMap);
source = removeMetadata(source, funcDepMap);
// Add `func` check to `createPartial`.
source = modifyFunction(source, 'createPartial', function(match) {
return match.replace(/^( *)(?=var\b)/m, function(match, indent) {
var prelude = '\n' + indent;
return indent + [
"if (typeof func != 'function') {",
' throw new TypeError(FUNC_ERROR_TEXT);',
'}'
].join(prelude) + prelude;
});
});
// Remove `apply` and `isSymbol`.
_.forOwn(funcDepMap, function(depNames, identifier) {
if (_.includes(depNames, 'apply')) {
_.pull(depNames, 'apply');
source = modifyFunction(source, identifier, function(match) {
return match.replace(/\bapply\(([^,)]+),\s*([^,)]+),\s*([^,)]+)\)/g, '$1.apply($2, $3)');
});
}
if (_.includes(depNames, 'isSymbol')) {
_.pull(depNames, 'isSymbol');
source = modifyFunction(source, identifier, function(match) {
return match.replace(/\bisSymbol\([^)]+\)/g, 'false');
});
}
});
// Remove chain dependencies.
_.pull(seqDepMap.funcDep,
'baseLodash', 'wrapperCommit', 'wrapperNext',
'wrapperPlant', 'wrapperToIterator'
);
// Remove ES5 and ES2015 built-in constructor deps.
_.forOwn(funcDepMap, function(deps) {
_.pull(deps, 'DataView', 'Map', 'Promise', 'Reflect', 'Set', 'Symbol', 'Uint8Array', 'WeakMap');
});
// Remove map, set, stack, and typed array support from `baseIsEqualDeep`.
_.pull(funcDepMap.baseIsEqualDeep, 'getTag', 'isBuffer', 'isTypedArray', 'Stack').push('baseGetTag', 'find');
source = modifyFunction(source, 'baseIsEqualDeep', function(match) {
return match
.replace(/\bgetTag\b/g, 'baseGetTag')
.replace(/\s*\|\|\s*isTypedArray\([^)]+\)/, '')
.replace(/^( *)if\s*\(.+?isBuffer\b[\s\S]+?\n\1\}\n/m, '')
.replace(/^( *)stack\s*\|\|[\s\S]+?\breturn\b([^;]+)(?=;\n)/gm, function(match, indent, compared) {
return indent + [
'var result =' + compared + ';',
"stack.pop();",
'return result'
].join('\n' + indent);
})
.replace(/^ *(?=if\s*\(isSameTag\b)/m, function(indent) {
return indent + [
'stack || (stack = []);',
'var objStack = find(stack, function(entry) {',
' return entry[0] == object;',
'});',
'var othStack = find(stack, function(entry) {',
' return entry[0] == other;',
'});',
'if (objStack && othStack) {',
' return objStack[1] == other;',
'}',
'stack.push([object, other]);',
'stack.push([other, object]);',
''
].join('\n' + indent);
});
});
// Remove `baseLodash` references.
_.each(['lodash', 'LodashWrapper'], function(funcName) {
_.pull(funcDepMap[funcName], 'baseLodash');
});
source = source.replace(/\bbaseLodash\.prototype\b/g, 'lodash.prototype');
// Remove `guard` check from `createAssigner`.
_.pull(funcDepMap.createAssigner, 'isIterateeCall');
source = modifyFunction(source, 'createAssigner', function(match) {
match = removeVar(match, 'guard');
return match.replace(/^(?: *\/\/.*\n)*( *)if\s*\(guard\b[^}]+?\n\1\}\n/m, '');
});
// Remove set cache use from `equalArrays`.
_.pull(funcDepMap.equalArrays, 'cacheHas', 'SetCache').push('indexOf');
source = modifyFunction(source, 'equalArrays', function(match) {
return match
.replace(/\bnew SetCache\b/, '[]')
.replace(/\bcacheHas\b/, 'indexOf')
.replace(/\badd\b/, 'push');
});
// Remove array buffer, data view, map, set, and symbol support from `equalByTag`.
_.pull(funcDepMap.equalByTag, 'mapToArray', 'setToArray');
source = modifyFunction(source, 'equalByTag', function(match) {
return match.replace(/^( *) case\s*(?:arrayBuffer|dataView|map|set|symbol)Tag\b[\s\S]+?(?=\s*case\b|\n\1\})/gm, '');
});
// Remove stack use from `equalArrays`, `equalByTag`, and `equalObjects`.
_.each(['equalArrays', 'equalByTag', 'equalObjects'], function(funcName) {
source = modifyFunction(source, funcName, function(match) {
match = removeVar(match, 'stacked');
return match
.replace(/^( *)if\s*\(stacked\b[^}]+?\n\1\}\n/m, '')
.replace(/^ *stack\.set\([^)]+\);\n/gm, '')
.replace(/^ *stack\['delete'\]\([^)]+\);\n/gm, '');
});
});
// Remove `customizer` use from `equalArrays` and `equalObjects`.
_.each(['equalArrays', 'equalObjects'], function(funcName) {
source = modifyFunction(source, funcName, function(match) {
return match.replace(/^( *)if\s*\(customizer\b[\s\S]+?\n\1}/m, '$1var compared;');
});
});
// Remove switch statements from functions.
_.each(['createCtor', 'negate'], function(funcName) {
source = modifyFunction(source, funcName, function(match) {
return match.replace(/^(?: *\/\/.*\n)*( *)switch\b[\s\S]+?\n\1\}\n/m, '');
});
});
// Remove `LazyWrapper` references.
_.pull(funcDepMap.baseWrapperValue, 'LazyWrapper');
source = modifyFunction(source, 'baseWrapperValue', function(match) {
return match.replace(/^( *)if\b.+?LazyWrapper\b[\s\S]+?\n\1}\n/m, '');
});
// Remove unneeded properties from `LodashWrapper.
source = modifyFunction(source, 'LodashWrapper', function(match) {
return match.replace(/^ *this\.__(?:index|values)__\s*=[^;]+;\n/gm, '');
});
// Replace array functions with their base counterparts.
_.each(['arrayEach', 'arrayFilter', 'arrayMap', 'arraySome', 'arrayReduce'], function(arrayName) {
var reArrayName = RegExp('\\b' + arrayName + '\\b', 'g');
var baseName = arrayName == 'arrayReduce'
? arrayName.replace(/^array/, '').toLowerCase()
: arrayName.replace(/^array/, 'base');
_.forOwn(funcDepMap, function(deps, funcName) {
if (_.includes(deps, arrayName)) {
_.pull(deps, arrayName).push(baseName);
source = modifyFunction(source, funcName, function(match) {
return match.replace(reArrayName, baseName);
});
}
});
});
// Replace `arrayEach` with `baseEach` in the chain scaffolding.
_.each([seqDepMap.funcDep, funcDepMap.mixin], function(depMap) {
_.pull(depMap, 'arrayEach').push('baseEach');
});
source = source.replace(/^(?: *\/\/.*\n)*( *)[$\w]+\(\['pop'[\s\S]+?\n\1\}\);\n/m, function(match) {
return match
.replace(/\barrayEach\b/g, 'baseEach')
.replace("'pop',", "$& 'join', 'replace', 'reverse', 'split',")
.replace(/\bpop\|/, '$&join|replace|')
.replace(/func\s*=[\s\S]+?(?=,\n)/, function() {
return 'func = (/^(?:replace|split)$/.test(methodName) ? String.prototype : arrayProto)[methodName]';
});
});
// Replace `getAllKeys` with `keys` in `equalObjects`.
_.pull(funcDepMap.equalObjects, 'getAllKeys').push('keys');
source = modifyFunction(source, 'equalObjects', function(match) {
return match.replace(/\bgetAllKeys\b/g, 'keys');
});
// Replace `baseAssign` with `assign` in `_.create`.
_.pull(funcDepMap.create, 'baseAssign').push('assign');
source = modifyFunction(source, 'create', function(match) {
return match.replace(/\bbaseAssign\b/g, 'assign');
});
// Replace `createWrap` with `_.partial` in `_.wrap`.
_.pull(funcDepMap.wrap, 'createWrap').push('partial');
source = modifyFunction(source, 'wrap', function(match) {
return match.replace(/\bcreateWrap\([\s\S]+?(?=;\n)/, 'partial(wrapper, value)');
});
// Replace type check methods with their base counterparts.
_.each(['isArrayBuffer', 'isDate', 'isMap', 'isRegExp', 'isSet', 'isTypedArray'], function(funcName) {
var baseName = 'base' + _.upperFirst(funcName);
funcDepMap[funcName] = [baseName];
varDepMap[funcName] = [];
source = replaceFunction(source, funcName, 'var ' + funcName + ' = ' + baseName + ';');
});
// Replace method implementations.
source = replaceFunction(source, 'arrayPush', [
'function arrayPush(array, values) {',
' array.push.apply(array, values);',
' return array;',
'}'
].join('\n'));
funcDepMap.baseAssignValue = [];
source = replaceFunction(source, 'baseAssignValue', [
'function baseAssignValue(object, key, value) {',
' object[key] = value;',
'}'
].join('\n'));
funcDepMap.baseGetTag = ['objectToString'];
source = replaceFunction(source, 'baseGetTag', [
'function baseGetTag(value) {',
' return objectToString(value);',
'}'
].join('\n'));
funcDepMap.baseIsArguments = ['noop'];
source = replaceFunction(source, 'baseIsArguments', 'var baseIsArguments = noop;');
funcDepMap.baseIteratee = ['baseMatches', 'baseProperty', 'identity'];
source = replaceFunction(source, 'baseIteratee', [
'function baseIteratee(func) {',
" if (typeof func == 'function') {",
' return func;',
' }',
' if (func == null) {',
' return identity;',
' }',
" return (typeof func == 'object' ? baseMatches : baseProperty)(func);",
'}'
].join('\n'));
funcDepMap.baseMatches = ['baseIsEqual', 'nativeKeys'];
source = replaceFunction(source, 'baseMatches', [
'function baseMatches(source) {',
' var props = nativeKeys(source);',
' return function(object) {',
' var length = props.length;',
' if (object == null) {',
' return !length;',
' }',
' object = Object(object);',
' while (length--) {',
' var key = props[length];',
' if (!(key in object &&',
' baseIsEqual(source[key], object[key], COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG)',
' )) {',
' return false;',
' }',
' }',
' return true;',
' };',
'}'
].join('\n'));
funcDepMap.basePick = ['reduce'];
source = replaceFunction(source, 'basePick', [
'function basePick(object, props) {',
' object = Object(object);',
' return reduce(props, function(result, key) {',
' if (key in object) {',
' result[key] = object[key];',
' }',
' return result;',
' }, {});',
'}'
].join('\n'));
funcDepMap.copyArray = ['baseSlice'];
source = replaceFunction(source, 'copyArray', [
'function copyArray(source) {',
' return baseSlice(source, 0, source.length);',
'}'
].join('\n'));
(function() {
var copyArray = matchFunction(source, 'copyArray', true);
source = removeFunction(source, 'copyArray');
source = modifyFunction(source, 'baseSlice', function(match) {
return match + '\n' + copyArray;
});
}());
source = replaceFunction(source, 'isFlattenable', [
'function isFlattenable(value) {',
' return isArray(value) || isArguments(value);',
'}'
].join('\n'));
funcDepMap.setToString = ['identity'];
source = replaceFunction(source, 'setToString', 'var setToString = identity;');
funcDepMap.wrapperClone = ['copyArray', 'LodashWrapper'];
source = replaceFunction(source, 'wrapperClone', [
'function wrapperClone(wrapper) {',
' var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__);',
' result.__actions__ = copyArray(wrapper.__actions__);',
' return result;',
'}'
].join('\n'));
funcDepMap.lodash = ['LodashWrapper'];
source = replaceFunction(source, 'lodash', [
'function lodash(value) {',
' return value instanceof LodashWrapper',
' ? value',
' : new LodashWrapper(value);',
'}'
].join('\n'));
funcDepMap.assign = ['createAssigner', 'copyObject', 'nativeKeys'];
source = replaceFunction(source, 'assign', [
'var assign = createAssigner(function(object, source) {',
' copyObject(source, nativeKeys(source), object);',
'});'
].join('\n'));
funcDepMap.assignIn = ['createAssigner', 'copyObject', 'nativeKeysIn'];
source = replaceFunction(source, 'assignIn', [
'var assignIn = createAssigner(function(object, source) {',
' copyObject(source, nativeKeysIn(source), object);',
'});'
].join('\n'));
funcDepMap.bind = ['baseRest', 'createPartial'];
source = replaceFunction(source, 'bind', [
'var bind = baseRest(function(func, thisArg, partials) {',
' return createPartial(func, WRAP_BIND_FLAG | WRAP_PARTIAL_FLAG, thisArg, partials);',
'});'
].join('\n'));
funcDepMap.clone = ['copyArray', 'copyObject', 'isArray', 'isObject', 'nativeKeys'];
source = replaceFunction(source, 'clone', [
'function clone(value) {',
' if (!isObject(value)) {',
' return value;',
' }',
' return isArray(value) ? copyArray(value) : copyObject(value, nativeKeys(value));',
'}'
].join('\n'));
funcDepMap.compact = ['baseFilter'];
source = replaceFunction(source, 'compact', [
'function compact(array) {',
' return baseFilter(array, Boolean);',
'}'
].join('\n'));
funcDepMap.filter = ['baseFilter', 'baseIteratee'];
source = replaceFunction(source, 'filter', [
'function filter(collection, predicate) {',
' return baseFilter(collection, baseIteratee(predicate));',
'}'
].join('\n'));
funcDepMap.forEach = ['baseIteratee', 'baseEach'];
source = replaceFunction(source, 'forEach', [
'function forEach(collection, iteratee) {',
' return baseEach(collection, baseIteratee(iteratee));',
'}'
].join('\n'));
funcDepMap.every = ['baseEvery', 'baseIteratee'];
source = replaceFunction(source, 'every', [
'function every(collection, predicate, guard) {',
' predicate = guard ? undefined : predicate;',
' return baseEvery(collection, baseIteratee(predicate));',
'}'
].join('\n'));
funcDepMap.has = [];
source = replaceFunction(source, 'has', [
'function has(object, path) {',
' return object != null && hasOwnProperty.call(object, path);',
'}'
].join('\n'));
funcDepMap.indexOf = [];
source = replaceFunction(source, 'indexOf', [
'function indexOf(array, value, fromIndex) {',
' var length = array == null ? 0 : array.length;',
" if (typeof fromIndex == 'number') {",
' fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : fromIndex;',
' } else {',
' fromIndex = 0;',
' }',
' var index = (fromIndex || 0) - 1,',
' isReflexive = value === value;',
'',
' while (++index < length) {',
' var other = array[index];',
' if ((isReflexive ? other === value : other !== other)) {',
' return index;',
' }',
' }',
' return -1;',
'}'
].join('\n'));
funcDepMap.invokeMap = ['baseMap', 'baseRest'];
source = replaceFunction(source, 'invokeMap', [
'var invokeMap = baseRest(function(collection, path, args) {',
" var isFunc = typeof path == 'function';",
' return baseMap(collection, function(value) {',
' var func = isFunc ? path : value[path];',
' return func == null ? func : func.apply(value, args);',
' });',
'});'
].join('\n'));
funcDepMap.isEmpty = ['isArguments', 'isArray', 'isArrayLike', 'isFunction', 'isString', 'nativeKeys'];
source = replaceFunction(source, 'isEmpty', [
'function isEmpty(value) {',
' if (isArrayLike(value) &&',
' (isArray(value) || isString(value) ||',
' isFunction(value.splice) || isArguments(value))) {',
' return !value.length;',
' }',
' return !nativeKeys(value).length;',
'}'
].join('\n'));
funcDepMap.iteratee = ['baseIteratee'];
source = replaceFunction(source, 'iteratee', 'var iteratee = baseIteratee;');
funcDepMap.keys = ['nativeKeys'];
source = replaceFunction(source, 'keys', 'var keys = nativeKeys;');
funcDepMap.keysIn = ['nativeKeysIn'];
source = replaceFunction(source, 'keysIn', 'var keysIn = nativeKeysIn;');
funcDepMap.map = ['baseMap', 'baseIteratee'];
source = replaceFunction(source, 'map', [
'function map(collection, iteratee) {',
' return baseMap(collection, baseIteratee(iteratee));',
'}'
].join('\n'));
funcDepMap.matches = ['assign', 'baseMatches'];
source = replaceFunction(source, 'matches', [
'function matches(source) {',
' return baseMatches(assign({}, source));',
'}'
].join('\n'));
funcDepMap.partial = ['baseRest', 'createPartial'];
source = replaceFunction(source, 'partial', [
'var partial = baseRest(function(func, partials) {',
' return createPartial(func, PARTIAL_FLAG, undefined, partials);',
'});'
].join('\n'));
funcDepMap.property = ['baseProperty'];
source = replaceFunction(source, 'property', 'var property = baseProperty;');
funcDepMap.reduce = ['baseEach', 'baseIteratee', 'baseReduce'];
source = replaceFunction(source, 'reduce', [
'function reduce(collection, iteratee, accumulator) {',
' return baseReduce(collection, baseIteratee(iteratee), accumulator, arguments.length < 3, baseEach);',
'}'
].join('\n'));
funcDepMap.result = ['isFunction'];
source = replaceFunction(source, 'result', [
'function result(object, path, defaultValue) {',
' var value = object == null ? undefined : object[path];',
' if (value === undefined) {',
' value = defaultValue;',
' }',
' return isFunction(value) ? value.call(object) : value;',
'}'
].join('\n'));
funcDepMap.size = ['isArrayLike', 'nativeKeys'];
source = replaceFunction(source, 'size', [
'function size(collection) {',
' if (collection == null) {',
' return 0;',
' }',
' collection = isArrayLike(collection) ? collection : nativeKeys(collection);',
' return collection.length;',
'}'
].join('\n'));
funcDepMap.slice = ['baseSlice'];
source = replaceFunction(source, 'slice', [
'function slice(array, start, end) {',
' var length = array == null ? 0 : array.length;',
' start = start == null ? 0 : +start;',
' end = end === undefined ? length : +end;',
' return length ? baseSlice(array, start, end) : [];',
'}'
].join('\n'));
funcDepMap.some = ['baseSome', 'baseIteratee'];
source = replaceFunction(source, 'some', [
'function some(collection, predicate, guard) {',
' predicate = guard ? undefined : predicate;',
' return baseSome(collection, baseIteratee(predicate));',
'}'
].join('\n'));
funcDepMap.sortBy = ['baseIteratee', 'baseMap', 'baseProperty', 'compareAscending'];
source = replaceFunction(source, 'sortBy', [
'function sortBy(collection, iteratee) {',
' var index = 0;',
' iteratee = baseIteratee(iteratee);',
'',
' return baseMap(baseMap(collection, function(value, key, collection) {',
" return { 'value': value, 'index': index++, 'criteria': iteratee(value, key, collection) };",
' }).sort(function(object, other) {',
' return compareAscending(object.criteria, other.criteria) || (object.index - other.index);',
" }), baseProperty('value'));",
'}'
].join('\n'));
funcDepMap.toArray = ['copyArray', 'isArrayLike', 'values'];
varDepMap.toArray = [];
source = replaceFunction(source, 'toArray', [
'function toArray(value) {',
' if (!isArrayLike(value)) {',
' return values(value);',
' }',
' return value.length ? copyArray(value) : [];',
'}'
].join('\n'));
funcDepMap.toInteger = [];
source = replaceFunction(source, 'toInteger', 'var toInteger = Number;');
funcDepMap.toNumber = [];
source = replaceFunction(source, 'toNumber', 'var toNumber = Number;');
funcDepMap.toString = [];
varDepMap.toString = [];
source = replaceFunction(source, 'toString', [
'function toString(value) {',
" if (typeof value == 'string') {",
' return value;',
' }',
" return value == null ? '' : (value + '');",
'}'
].join('\n'));
}
// Redistribute `seqDepMap` deps.
if (isModularize) {
funcDepMap.main = _.union(funcDepMap.main, _.difference(seqDepMap.funcDep, _.without(mapping.category.Seq, 'thru')));
varDepMap.main = _.union(varDepMap.main, seqDepMap.varDep);
}
else {
funcDepMap.wrapperValue = _.union(funcDepMap.wrapperValue, _.without(seqDepMap.funcDep, 'lodash', 'LodashWrapper'));
varDepMap.wrapperValue = _.union(varDepMap.wrapperValue, seqDepMap.varDep);
_.each(_.without(mapping.category.Seq, 'lodash', 'wrapperValue'), function(funcName) {
funcDepMap[funcName].push('wrapperValue');
});
}
if (isModularize ||
(isCore && _.isEmpty(plusFuncs))) {
source = removeGetIteratee(source, funcDepMap);
}
// Remove iteratee shorthand support from `iteratee`.
if (_.isEmpty(_.difference(['matches', 'matchesProperty', 'property'], minusFuncs))) {
_.pull(funcDepMap.iteratee, 'baseClone');
source = modifyFunction(source, 'iteratee', function(match) {
return match.replace(/^( *return\s+)[\s\S]+?(?=;\n)/m, '$1baseIteratee(func)');
});
}
// Remove individual iteratee shorthands from `baseIteratee`.
_.each(['baseMatches', 'baseMatchesProperty', 'property'], function(funcName) {
var otherName = _.camelCase(_.trimStart(funcName, 'base'));
if (_.includes(minusFuncs, otherName)) {
_.pull(funcDepMap.baseIteratee, funcName);
source = modifyFunction(source, 'baseIteratee', function(match) {
return match.replace(RegExp('\\b' + funcName + '\\([\\s\\S]+?\\)(?=[;\\n])', 'm'), 'identity');
});
}
});
// Add core function names.
if (isCore) {
var result = _.clone(listing.coreFuncs);
}
// Add function names explicitly.
else if (!_.isEmpty(includeFuncs)) {
result = includeFuncs;
}
// Add default function names.
else if (_.isEmpty(includeVars)) {
result = _.clone(listing.includes);
}
// Remove special "none" entry.
_.pull(result, 'none');
// Force removing or adding deps.
if (isModularize) {
minusFuncs.push('noConflict', 'runInContext');
} else {
plusFuncs.push('lodash');
}
// Add extra function names.
if (!_.isEmpty(plusFuncs)) {
result = _.union(result, plusFuncs);
}
// Subtract function names.
if (!_.isEmpty(minusFuncs)) {
result = _.difference(result, minusFuncs.concat(getDependants(minusFuncs, funcDepMap, true)));
}
if (!isModularize) {
result.push('main');
}
result = _.uniq(result);
var hasLodash = _.includes(result, 'lodash');
result = getDependencies(result, funcDepMap, true, ['lodash']);
// Simplify `lodash` when not capable of chaining.
if (!_.includes(result, 'mixin') && !_.includes(result, 'wrapperValue')) {
funcDepMap.lodash.length = 0;
source = replaceFunction(source, 'lodash', [
'function lodash() {',
' // No operation performed.',
'}'
].join('\n'));
}
if (hasLodash) {
result = _.union(result, ['lodash'], getDependencies('lodash', funcDepMap, true));
}
return result;
}());
// Expand properties, variables, and their function dependencies to include in the build.
var allDeps = getAllDependencies(buildFuncs, funcDepMap, varDepMap).sort();
buildFuncs = _.intersection(listing.funcs, allDeps);
includeVars = _.intersection(listing.varDeps, allDeps);
/*------------------------------------------------------------------------*/
// Load customized lodash module.
var lodash = !isTemplate && (function() {
var context = vm.createContext({
'console': console
});
vm.runInContext(source, context);
return context._;
}());
/*------------------------------------------------------------------------*/
if (isTemplate) {
source = buildTemplate({
'moduleId': moduleId,
'source': source,
'templatePattern': templatePattern,
'templateSettings': templateSettings
});
}
else if (isModularize) {
// Replace the `lodash.templateSettings` property assignment with a variable assignment.
source = source.replace(/\b(lodash\.)(?=templateSettings\s*=)/, 'var ');
// Remove the `lodash` namespace from properties.
source = source.replace(/( *)lodash\.([$\w]+\.)?([$\w]+)(\s*=\s*(?:function\([\s\S]+?\n\1\}|.+?);\n)?/g, function(match, indent, property, identifier, right) {
return (property || right || identifier == 'com' || identifier == 'prototype')
? match
: (indent + identifier);
});
// Remove all horizontal rule comment separators.
source = source.replace(/^ *\/\*-+\*\/\n/gm, '');
// Remove `lodash` branch in `_.mixin`.
source = modifyFunction(source, 'mixin', function(match) {
return match
.replace(/^(?: *\/\/.*\n)*( *)if\s*\(options\s*==\s*null\b[\s\S]+?\n\1\}\n/m, '')
.replace(/^(?: *\/\/.*\n)*( *)if\s*\(!methodNames\b[\s\S]+?\n\1\}/m, function(match, indent) {
return indent + 'var methodNames = baseFunctions(source, keys(source));\n';
});
});
// Replace `lodash` use in `_.templateSettings.imports`.
source = modifyProp(source, 'templateSettings', function(match) {
return match.replace(/(:\s*)lodash\b/, "$1{ 'escape': escape }");
});
source = modifyFunction(source, 'template', function(match) {
// Assign `settings` using `template.imports`.
match = match.replace(/=\s*templateSettings(?=[,;])/, '$&.imports._.templateSettings || templateSettings');
// Remove default `sourceURL` value.
return modifyVar(match, 'sourceURL', function(match) {
return match.replace(/^( *)(var\s+sourceURL\s*=\s*)[\s\S]+?(?=;\n$)/m, function(submatch, indent, left) {
return indent + left + [
"hasOwnProperty.call(options, 'sourceURL')",
indent + " ? ('//# sourceURL=' +",
indent + " (options.sourceURL + '').replace(/\\s/g, ' ') +",
indent + " '\\n')",
indent + " : ''"
].join('\n');
});
});
});
if (!isAMD) {
source = removeVar(source, 'undefined');
}
}
if (!isTemplate) {
// Remove functions and method assignments from the build.
_.each(_.difference(listing.funcs, buildFuncs), function(funcName) {
source = removeFunction(source, funcName);
if (!isModularize) {
source = removeMethodAssignment(source, funcName);
}
});
// Remove method assignments for non-core methods.
if (isCore) {
_.each(_.difference(buildFuncs, listing.coreFuncs, plusFuncs), function(funcName) {
source = removeMethodAssignment(source, funcName);
});
}
// Tokenize functions to reduce modularized build times.
if (isModularize) {
funcTokenMap = _.transform(buildFuncs, function(result, funcName) {
var match = matchFunction(source, funcName, true);
source = replace(source, match,
_.get(/^,\s+/.exec(match), 0, '') +
'<<' + funcName + '>>' +
_.get(/(?:,?\s+|\n)$/.exec(match), 0, '')
);
result[funcName] = match;
}, new Hash);
}
// Detect variables after tokenizing functions.
varNames = _.difference(getVars(source), buildFuncs.concat(includeVars, 'freeGlobal'));
}
/*------------------------------------------------------------------------*/
(function() {
var snippet = isTemplate ? source : source.slice(-(getMethodAssignments(source), RegExp.rightContext.length)),
modified = snippet;
// Set the AMD module ID.
if (isAMD && !isModularize && !isTemplate && moduleId != null && moduleId != 'none') {
modified = modified.replace(/^ *define\((?=function)/m, "$&'" + moduleId + "', ");
}
// Customize lodash's exports.
if (!isAMD || isModularize) {
modified = modified.replace(/^(?: *\/\/.*\n)*( *)if\s*\(typeof\s+define\b[\s\S]+?else\s+/m, '$1');
}
if (!isNode || isModularize) {
modified = modified.replace(/^(?: *\/\/.*\n)*( *)(?:else\s+)?if\s*\(freeModule\b[\s\S]+?\n\1\}\n+/m, '');
}
if (!isGlobal || isModularize) {
if (isAMD && !isModularize && !isTemplate) {
modified = modified.replace(/^(?: *\/\/.*\n)* *root\._\s*=[\s\S]+?\n+/m, '');
}
modified = modified.replace(/^(?: *\/\/.*\n)*( *)else\b[^}]+?(?:\._|_\.templates)\s*=[\s\S]+?\n\1\}\n+/m, '');
}
else if (!isAMD && !isNode) {
modified = modified
.replace(/^( *)else\s+(?=if\s*\(_\))/m, '$1')
.replace(/^( *)else\s\{\s*([^}]+?\._\s*=[\s\S]+?)\n\1\}/m, function(match, indent, block) {
return indent + block.replace(/\n\s*/g, '\n' + indent);
});
}
source = isTemplate ? modified : source.replace(snippet, modified);
}());
/*------------------------------------------------------------------------*/
source = cleanupSource(source);
// Exit early to create modules.
if (isModularize) {
if (callback == defaultBuildCallback) {
callback = null;
}
buildModule({
'buildFuncs': buildFuncs,
'filePath': filePath,
'funcDepMap': funcDepMap,
'funcTokenMap': funcTokenMap,
'includeFuncs': includeFuncs,
'includeVars': includeVars,
'isAMD': isAMD,
'isES': isES,
'isNode': isNode,
'isNpm': isNpm,
'isSilent': isSilent,
'isStdOut': isStdOut,
'isStrict': isStrict,
'lodash': lodash,
'minusFuncs': minusFuncs,
'options': options,
'outputPath': outputPath,
'plusFuncs': plusFuncs,
'source': source,
'stamp': stamp,
'varDepMap': varDepMap,
'varNames': varNames
}, callback);
return;
}
}
/*--------------------------------------------------------------------------*/
// Modify/remove references to removed functions/variables.
if (!isTemplate) {
// Remove prototype assignments.
_.each(['Hash', 'ListCache', 'MapCache', 'SetCache', 'Stack'], function(funcName) {
if (isExcluded(funcName)) {
source = source.replace(RegExp('^(?: *\\/\\/.*\\n)* *' + funcName + '\\.prototype(?:\\.[$\\w]+|\\[\'[^\']+\'\\])\\s*=[^;]+;\\n', 'gm'), '');
}
});
if (isExcluded('getTag')) {
source = removeGetTagFork(source);
}
if (isExcluded('LazyWrapper')) {
// Remove `LazyWrapper.prototype` assignment.
source = source.replace(/^(?: *\/\/.*\n)* *LazyWrapper\.prototype(\.constructor)?\s*=[^;]+;\n/gm, '');
}
if (isExcluded('lodash') || !_.includes(funcDepMap.lodash, 'baseLodash')) {
// Remove `lodash.prototype` assignment.
source = source.replace(/^(?: *\/\/.*\n)* *lodash\.prototype(\.constructor)?\s*=[^;]+;\n/gm, '');
}
if (isExcluded('LodashWrapper')) {
// Remove `LodashWrapper.prototype` assignment.
source = source.replace(/^(?: *\/\/.*\n)* *LodashWrapper\.prototype(\.constructor)?\s*=[^;]+;\n/gm, '');
}
if (isExcluded('memoize')) {
// Remove `memoize.Cache` assignment.
source = source.replace(/^(?: *\/\/.*\n)* *memoize\.Cache\s*=[^;]+;\n/m, '');
}
if (isExcluded(isModularize ? 'main' : 'wrapperToIterator')) {
// Remove conditional `wrapperToIterator` assignment.
source = source.replace(/^(?: *\/\/.*\n)*( *)if\s*\(symIterator\)[\s\S]+?\n\1\}\n/gm, '');
}
if (isExcluded(isModularize ? 'main' : 'mixin')) {
source = removeMixinCalls(source);
}
if (isExcluded(isModularize ? 'main' : 'wrapperValue')) {
source = removeChaining(source);
}
if (isModularize && isExcluded('main')) {
source = removeAssignments(source);
}
// Remove placeholder assignments of removed functions.
source = source.replace(/^((?: *\/\/.*\n)*)( *)(arrayEach\(\[')bind\b[\s\S]+?('\][\s\S]+?\n\2\}\))/m, function(match, comment, indent, prelude, postlude) {
var funcNames = _.reject(listing.placeholderFuncs, isExcluded),
length = isCore ? 0 : funcNames.length;
if (length) {
return length > 1
? (comment + indent + prelude + funcNames.join("', '") + postlude)
: (comment + indent + funcNames[0] + '.placeholder = ') + (isModularize ? '{}' : 'lodash');
}
return '';
});
// Remove unused variable dependencies.
_.each(_.difference(listing.varDeps, includeVars.concat('root')), function(varName) {
source = removeVar(source, varName);
});
if (!_.includes(includeVars, 'root')) {
varNames = _.without(varNames, 'root');
source = removeVar(source, 'root');
}
if (!_.includes(includeVars, 'templateSettings')) {
varNames = _.without(varNames, 'templateSettings');
source = removeProp(source, 'templateSettings');
}
if (isModularize) {
// Untokenize functions to include in the build.
_.each(buildFuncs, function(funcName) {
source = source.replace(RegExp(',\\s+<<' + funcName + '>>|<<' + funcName + '>>(,\\s+|\\n)?'), function() {
return funcTokenMap[funcName];
});
});
source = source.replace(consts.reNamedToken, '');
}
// Remove unused variables.
source = stringFree(source, function(source) {
return commentFree(source, function(source) {
var useMap = new Hash;
while (varNames.length) {
varNames = _.sortBy(varNames, function(varName) {
var result = isVarUsed(source, varName);
useMap[varName] = result;
return result;
});
if (useMap[varNames[0]]) {
break;
}
while (varNames.length && !useMap[varNames[0]]) {
source = removeVar(source, varNames.shift());
}
}
return source;
});
});
}
// Customize lodash's IIFE.
if (isIIFE) {
source = replaceIIFE(source, iife);
}
/*--------------------------------------------------------------------------*/
source = cleanupSource(source);
// A flag to track if `outputPath` has been used by `callback`.
var outputUsed = false;
// Expand `outputPath` and create directories if needed.
if (outputPath) {
outputPath = (function() {
var dirname = path.dirname(outputPath);
fs.mkdirpSync(dirname);
return path.join(fs.realpathSync(dirname), path.basename(outputPath));
}());
}
// Resolve the basename of the output path.
var basename = filePath = outputPath;
if (!basename) {
basename = 'lodash';
if (isTemplate) {
basename += '.templates';
} else if (isCustom) {
basename += '.custom';
}
filePath = path.join(cwd, basename + '.js');
}
// Output development build.
if (!isProduction) {
var devSource = source;
if (isCustom) {
devSource = addCommandsToHeader(devSource, options);
}
if (isDevelopment && isStdOut) {
stdout.write(devSource);
callback({
'source': devSource
});
}
else if (!isStdOut) {
outputUsed = true;
callback({
'source': devSource,
'outputPath': filePath
});
}
}
// Begin the minification process.
if (!isDevelopment) {
if (outputPath && outputUsed) {
outputPath = path.join(path.dirname(outputPath), path.basename(outputPath, '.js') + '.min.js');
} else if (!outputPath) {
outputPath = path.join(cwd, basename + '.min.js');
}
minify(source, {
'filePath': filePath,
'isMapped': isMapped,
'isSilent': isSilent,
'isTemplate': isTemplate,
'modes': isIIFE ? ['simple', 'hybrid'] : ['simple', 'advanced', 'hybrid'],
'outputPath': outputPath,
'sourceMapURL': sourceMapURL,
'onComplete': function(data) {
if (isCustom) {
data.source = addCommandsToHeader(data.source, options);
}
if (isStdOut) {
delete data.outputPath;
stdout.write(data.source);
}
callback(data);
}
});
}
}
/*----------------------------------------------------------------------------*/
// Export `build`.
if (!isBin) {
module.exports = build;
}
// Handle invoking `build` by the command-line.
else if (_.size(process.argv) > 2) {
build(process.argv.slice(2));
}
|