systemjs-builder
Version:
SystemJS Build Tool
857 lines (721 loc) • 28.8 kB
JavaScript
var Promise = require('bluebird');
var System = require('systemjs');
var asp = require('bluebird').promisify;
var fs = require('fs');
var path = require('path');
var extend = require('./utils').extend;
var attachCompilers = require('./compile').attachCompilers;
var compileTree = require('./compile').compileTree;
var compileLoad = require('./compile').compileLoad;
var pluginBundleHook = require('./compile').pluginBundleHook;
var writeOutputs = require('./output').writeOutputs;
var traceExpression = require('./arithmetic').traceExpression;
var Trace = require('./trace');
var getCanonicalName = require('./utils').getCanonicalName;
var fromFileURL = require('./utils').fromFileURL;
var toFileURL = require('./utils').toFileURL;
var createHash = require('crypto').createHash;
var getFormatHint = require('./utils').getFormatHint;
// new Builder(baseURL)
// new Builder({cfg})
// new Builder(baseURL, {cfg})
// new Builder(baseURL, 'cfg-file.js')
//
// baseURL is ignored in cfg when given
// cfg is saved and used by reset
function Builder(baseURL, cfg) {
if (typeof baseURL == 'object') {
cfg = baseURL;
baseURL = null;
}
this.loader = null;
this.resetConfig = function() {};
this.reset();
if (baseURL)
this.config({ baseURL: baseURL });
// config passed to constructor will
// be saved for future .reset() calls
if (typeof cfg == 'object')
this.config(cfg, true, !!baseURL);
else if (typeof cfg == 'string')
this.loadConfigSync(cfg, true, !!baseURL);
}
// invalidate the cache for a given module name
// accepts wildcards (*) which are taken to be deep-matching
Builder.prototype.invalidate = function(invalidationModuleName) {
var loader = this.loader;
var invalidated = [];
var self = this;
// invalidation happens in normalized-space
invalidationModuleName = loader.decanonicalize(invalidationModuleName);
// wildcard detection and handling
var invalidationWildcardIndex = invalidationModuleName.indexOf('*');
if (invalidationModuleName.lastIndexOf('*') != invalidationWildcardIndex)
throw new TypeError('Only a single wildcard supported for invalidation.');
if (invalidationWildcardIndex != -1) {
var wildcardLHS = invalidationModuleName.substr(0, invalidationWildcardIndex);
var wildcardRHS = invalidationModuleName.substr(invalidationWildcardIndex + 1);
}
function matchesInvalidation(moduleName) {
if (moduleName == invalidationModuleName)
return true;
if (invalidationWildcardIndex == -1)
return false;
return moduleName.substr(0, invalidationWildcardIndex) == wildcardLHS
&& moduleName.substr(moduleName.length - wildcardRHS.length) == wildcardRHS;
}
// invalidate the given path in the trace cache
var traceCache = this.cache.trace;
Object.keys(traceCache).forEach(function(canonical) {
var moduleName = loader.decanonicalize(canonical);
if (matchesInvalidation(moduleName)) {
if (traceCache[canonical])
invalidated.push(moduleName);
traceCache[canonical] = undefined;
}
});
// clear the given path from the pluginLoader registry
var pluginLoader = loader.pluginLoader;
Object.keys(pluginLoader._loader.modules).forEach(function(moduleName) {
if (matchesInvalidation(moduleName)) {
invalidated.push(moduleName);
pluginLoader.delete(moduleName);
}
});
// clear the loader define cache
loader.defined = {};
// we leave the compile cache in-tact as it is hashed
// return array of invalidated canonicals
return invalidated;
};
Builder.prototype.reset = function(baseLoader) {
baseLoader = baseLoader || this.loader || System;
var loader = this.loader = new baseLoader.constructor();
// make builder accessible in plugins
loader.builder = this;
// put the loader in the builder environment
loader.config({ build: true });
loader.import = function(name) {
return Promise.reject(new Error('Unable to import "' + name + '".\nThe incorrect instance of System is being used to System.import.'));
};
// ensure this loader doesn't attempt to load the runtime
loader._loader.loadedTranspilerRuntime = true;
var pluginLoader = loader.pluginLoader = new baseLoader.constructor();
pluginLoader.config({ build: true });
loader.constructor = pluginLoader.constructor = baseLoader.constructor;
loader.baseURL = pluginLoader.baseURL = baseLoader.baseURL;
loader.normalize = pluginLoader.normalize = baseLoader.normalize;
loader.normalizeSync = pluginLoader.normalizeSync = baseLoader.normalizeSync;
// store original hooks for next reset
loader.originalHooks = baseLoader.originalHooks || {
locate: baseLoader.locate,
fetch: baseLoader.fetch,
translate: baseLoader.translate,
instantiate: baseLoader.instantiate
};
loader.locate = pluginLoader.locate = loader.originalHooks.locate;
loader.fetch = pluginLoader.fetch = loader.originalHooks.fetch;
loader.translate = pluginLoader.translate = loader.originalHooks.translate;
loader.instantiate = pluginLoader.instantiate = loader.originalHooks.instantiate;
var loaderConfig = loader.config;
loader.config = function(cfg) {
// plugin loader is dev, Node environment
loader.pluginLoader.config(cfg);
var lCfg = extend({}, cfg);
loaderConfig.call(this, lCfg);
loader.configHash = generateConfigHash(loader);
};
this.getCanonicalName = getCanonicalName.bind(null, this.loader);
this.loader.getCanonicalName = this.loader.pluginLoader.getCanonicalName = this.getCanonicalName;
attachCompilers(loader);
var builder = this;
// allow a custom fetch hook
var loaderFetch = loader.fetch;
var cachedFetch = function(load) {
return Promise.resolve(loaderFetch.call(this, load))
.then(function(source) {
// calling default fs.readFile fetch -> set timestamp as well for cache invalidation
return asp(fs.stat)(fromFileURL(load.address))
.then(function(stats) {
load.metadata.timestamp = stats.mtime.getTime();
return source;
}, function(err) {
// if the stat fails on a plugin, it may not be linked to the file itself
if (err.code == 'ENOENT' && load.metadata.loader)
return source;
throw err;
});
});
};
loader.fetch = function(load) {
if (builder.fetch)
return Promise.resolve(builder.fetch.call(this, load, cachedFetch.bind(this)));
else
return cachedFetch.call(this, load);
};
// this allows us to normalize package conditionals into package conditionals
// package environment normalization handling for fallbacks,
// which dont resolve in the loader itself unlike other conditionals which
// have this condition inlined under loader.builder in conditionals.js
var loaderNormalize = loader.normalize;
loader.normalize = function(name, parentName, parentAddress) {
var pkgConditional;
var pkgConditionalIndex = name.indexOf('/#:');
if (pkgConditionalIndex != -1) {
pkgConditional = name.substr(pkgConditionalIndex);
name = name.substr(0, pkgConditionalIndex + 1);
}
return loaderNormalize.call(this, name, parentName, parentAddress)
.then(function(normalized) {
if (pkgConditional)
normalized = normalized + pkgConditional;
return normalized;
});
};
var loaderNormalizeSync = loader.normalizeSync;
loader.normalizeSync = function(name, parentName, parentAddress) {
var pkgConditional;
var pkgConditionalIndex = name.indexOf('#:');
if (pkgConditionalIndex != -1) {
pkgConditional = name.substr(pkgConditionalIndex);
name = name.substr(0, pkgConditionalIndex) + '/';
}
var normalized = loaderNormalizeSync.call(this, name, parentName, parentAddress);
if (pkgConditional)
normalized = normalized.substr(0, normalized.length - 1) + pkgConditional;
return normalized;
};
// run saved configuration functions against new loader instance
this.resetConfig();
// create cache if not existing
this.setCache(this.cache || {});
// mark all traces as unfresh
var traceCache = this.cache.trace;
Object.keys(traceCache).forEach(function(canonical) {
if (traceCache[canonical])
traceCache[canonical].fresh = false;
});
};
function generateConfigHash(loader) {
return createHash('md5')
.update(JSON.stringify({
paths: loader.paths,
packages: loader.packages,
meta: loader.meta,
map: loader.map
}))
.digest('hex');
}
Builder.prototype.setCache = function(cacheObj) {
this.cache = {
compile: cacheObj.compile || {},
trace: cacheObj.trace || {}
};
this.cache.compile.encodings = this.cache.compile.encodings || {};
this.cache.compile.loads = this.cache.compile.loads || {};
this.tracer = new Trace(this.loader, this.cache.trace);
};
Builder.prototype.getCache = function() {
return this.cache;
};
Builder.prototype.clearCache = function() {
this.setCache({});
};
function executeConfigFile(saveForReset, ignoreBaseURL, configPath, source) {
var self = this;
// create a safe loader to give to the config execution
var configLoader = Object.create(this.loader);
configLoader.config = function(cfg) {
self.config(cfg, saveForReset, ignoreBaseURL);
};
// make everything in global available to config file code
// only substitute local copy of System
// and prevent that code from adding anything to the real global
var context = Object.create(global);
context.SystemJS = context.System = configLoader;
context.global = context;
if (process.versions.node && process.versions.node.split('.')[0] < 6)
context.GLOBAL = context.root = context;
// make require available too, make it look as if config file was
// loaded like a module
var Module = require('module');
var m = new Module(configPath);
m.filename = configPath;
m.paths = Module._nodeModulePaths(path.dirname(configPath));
context.require = m.require.bind(m);
require('vm').runInNewContext(source.toString(), context);
}
Builder.prototype.loadConfig = function(configFile, saveForReset, ignoreBaseURL) {
return asp(fs.readFile)(configFile)
.then(executeConfigFile.bind(this, saveForReset, ignoreBaseURL, configFile));
};
Builder.prototype.loadConfigSync = function(configFile, saveForReset, ignoreBaseURL) {
var source = fs.readFileSync(configFile);
executeConfigFile.call(this, saveForReset, ignoreBaseURL, configFile, source);
};
// note ignore argument is part of API
Builder.prototype.config = function(config, saveForReset, ignoreBaseURL) {
var cfg = {};
for (var p in config) {
if (ignoreBaseURL && p == 'baseURL' || p == 'bundles' || p == 'depCache')
continue;
cfg[p] = config[p];
}
this.loader.config(cfg);
if (saveForReset) {
// multiple config calls can be saved for reset
var curReset = this.resetConfig;
this.resetConfig = function() {
curReset.call(this);
this.loader.config(cfg);
};
}
};
/*
* Builder Operations
*/
function processTraceOpts(options, defaults) {
var opts = {
// indicate to plugins to output ES modules instead of System.register for Rollup support
outputESM: true,
sourceMaps: false,
// conditional tracing options
browser: undefined,
node: undefined,
production: undefined,
dev: undefined,
conditions: {
'@system-env|default': true,
'@system-env|~default': false
},
inlineConditions: {},
traceRuntimePlugin: true,
// package config tracing (used for bundles only)
tracePackageConfig: false,
// when set, automatically excludes non-file URLs that don't resolve to canonical names
// instead of showing the "Unable to calculate canonical name" error
excludeURLs: true,
externals: []
};
// ensure user environment overrides default environment
if (typeof options == 'object') {
if (typeof options.browser == 'boolean' || typeof options.node == 'boolean') {
// browser true/false -> node is opposite
if (typeof options.browser == 'boolean' && typeof options.node != 'boolean')
options.node = !options.browser;
// node true/false -> browser is opposite
else if (typeof options.node == 'boolean' && typeof options.browser != 'boolean')
options.browser = !options.node;
}
// development -> dev backwards compat
if ('development' in options)
options.dev = options.development;
if (typeof options.dev == 'boolean' || typeof options.production == 'boolean') {
if (typeof options.production != 'boolean')
options.production = !options.dev;
if (typeof options.dev != 'boolean')
options.dev = !options.production;
}
}
extend(opts, defaults);
extend(opts, options);
// globalDeps are externals
if (opts.globalDeps)
opts.externals = opts.externals.concat(Object.keys(opts.globalDeps));
// conditional tracing defaults
if (typeof opts.browser == 'boolean') {
// browser, node -> browser, ~browser, node, ~node
// !browser, node -> ~browser, node
// browser, !node -> browser, ~node
// !browser, !node -> ~browser, ~node
opts.conditions['@system-env|browser'] = opts.browser;
opts.conditions['@system-env|~browser'] = opts.browser === true ? opts.node : !opts.browser;
opts.conditions['@system-env|node'] = opts.node;
opts.conditions['@system-env|~node'] = opts.node === true ? opts.browser : !opts.node;
}
if (typeof opts.production == 'boolean') {
opts.conditions['@system-env|production'] = opts.production;
opts.conditions['@system-env|~production'] = opts.dev;
opts.conditions['@system-env|dev'] = opts.dev;
opts.conditions['@system-env|~dev'] = opts.production;
}
if (typeof opts.inlineConditions == 'object')
extend(opts.conditions, opts.inlineConditions);
else if (opts.inlineConditions === true)
opts.inlineConditions = opts.conditions;
else
opts.inlineConditions = {};
return opts;
}
Builder.prototype.trace = function(expression, opts) {
if (opts && opts.config)
this.config(opts.config);
var traceOpts = processTraceOpts(opts);
var self = this;
return canonicalizeConditions(self.loader, traceOpts)
.then(function() {
return setExternals(self, traceOpts.externals);
})
.then(function() {
return traceExpression(self, expression, traceOpts);
});
};
Builder.prototype.traceConditionalEnv = function(expression, opts) {
var traceOpts = processTraceOpts(opts);
var self = this;
return canonicalizeConditions(self.loader, traceOpts)
.then(function() {
return self.trace(expression, traceOpts);
})
.then(function(tree) {
return Trace.getConditionalEnv(self, tree, traceOpts);
});
}
function processCompileOpts(options, defaults) {
// NB deprecate these warnings
// all of the below features were undocumented and experimental, so deprecation needn't be long
options = options || {};
if ('sfxFormat' in options) {
console.warn('SystemJS Builder "sfxFormat" is deprecated and has been renamed to "format".');
options.format = options.sfxFormat;
}
if ('sfxEncoding' in options) {
console.warn('SystemJS Builder "sfxEncoding" is deprecated and has been renamed to "encodeNames".');
options.encodeNames = sfxEncoding;
}
if ('sfxGlobals' in options) {
console.warn('SystemJS Builder "sfxGlobals" is deprecated and has been renamed to "globalDeps".');
options.globalDeps = options.sfxGlobals;
}
if ('sfxGlobalName' in options) {
console.warn('SystemJS Builder "sfxGlobalName" is deprecated and has been renamed to "globalName".');
options.globalName = options.sfxGlobalName;
}
var opts = {
entryPoints: [],
normalize: false,
anonymous: false,
systemGlobal: 'System',
// whether to inline package configurations into bundles
buildConfig: false,
inlinePlugins: true,
static: false,
encodeNames: undefined,
sourceMaps: false,
lowResSourceMaps: false,
// static build options
// it may make sense to split this out into build options
// at a later point as static build and bundle compile diverge further
runtime: false,
format: 'umd',
globalDeps: {},
globalName: null,
exportDefault: false,
// conditionalResolutions: {},
// can add a special object here that matches condition predicates to direct module names to use for sfx
// set to true to build in "format amd" style sfx hints for SystemJS loading
formatHint: false,
// this shouldn't strictly be a compile option (its a trace option)
// but the cjs compiler needs it to do NODE_ENV optimization
production: undefined,
// use rollup optimizations
rollup: true
};
extend(opts, defaults);
extend(opts, options);
if (options.encodeNames && !('encodeNames' in defaults))
throw new Error('encodeNames is only supported for buildStatic.');
if (typeof opts.production != 'boolean')
opts.production = !opts.development;
if (opts.static) {
if (opts.encodeNames !== false)
opts.encodeNames = true;
// include runtime by default if needed
if (options.runtime !== false)
opts.runtime = true;
// static builds have a System closure with a dummy name
opts.systemGlobal = '$__System';
// static builds normalize all requires
opts.normalize = true;
}
return opts;
}
Builder.prototype.compile = function(moduleNameOrLoad, outFile, opts) {
if (outFile && typeof outFile == 'object') {
opts = outFile;
outFile = undefined;
}
if (opts && opts.config)
this.config(opts.config);
var self = this;
var traceOpts = processTraceOpts(opts, { tracePackageConfig: false, browser: true, node: false, production: true, dev: false, outputESM: false });
return Promise.resolve()
.then(function() {
return canonicalizeConditions(self.loader, traceOpts);
})
.then(function() {
if (typeof moduleNameOrLoad != 'string')
return moduleNameOrLoad;
return self.loader.normalize(moduleNameOrLoad)
.then(function(moduleName) {
if (!opts || opts.cache !== true)
self.invalidate(moduleName);
return self.tracer.getLoadRecord(getCanonicalName(self.loader, moduleName), traceOpts);
});
})
.then(function(load) {
var compileOpts = processCompileOpts(opts, { normalize: false, anonymous: true });
var outputOpts = processOutputOpts(opts, { outFile: outFile });
return Promise.resolve()
.then(function() {
return compileLoad(self.loader, load, processCompileOpts(opts, { normalize: false, anonymous: true }), self.cache.compile);
})
.then(function(output) {
if (load.metadata.loader)
return pluginBundleHook(self.loader, [load], compileOpts, outputOpts)
.then(function(bundleOutput) {
return [output].concat(bundleOutput.outputs);
});
return [output];
})
.then(function(outputs) {
return writeOutputs(outputs, self.loader.baseURL, outputOpts);
});
});
};
function processOutputOpts(options, defaults) {
var opts = {
outFile: undefined,
minify: false,
uglify: undefined,
mangle: true,
sourceMaps: false,
sourceMapContents: undefined
};
extend(opts, defaults);
extend(opts, options);
opts.uglify = opts.uglify || {};
opts.uglify.output = opts.uglify.output || {};
opts.uglify.compress = opts.uglify.compress || {};
opts.uglify.beautify = opts.uglify.beautify || opts.uglify.output;
// NB deprecated these for uglify directly
if (opts.globalDefs && !('global_defs' in opts.uglify.compress))
opts.uglify.compress.global_defs = opts.globalDefs;
if (opts.ascii && !('ascii' in opts.uglify.beautify))
opts.uglify.beautify.ascii_only = opts.ascii;
delete opts.globalDefs;
delete opts.ascii;
if (!('dead_code' in opts.uglify.compress))
opts.uglify.compress.dead_code = true;
if (!('warnings' in opts.uglify.compress))
opts.uglify.compress.warnings = false;
// source maps 'inline' handling
if (opts.sourceMapContents === undefined)
opts.sourceMapContents = opts.sourceMaps == 'inline';
if (opts.sourceMapContents)
opts.uglify.sourceMapIncludeSources = true;
return opts;
}
function setExternals(builder, externals) {
if (!externals)
return Promise.resolve();
var externalMeta;
return Promise.all(externals.map(function(external) {
externalMeta = externalMeta || {};
return builder.loader.normalize(external)
.then(function(normalizedExternal) {
externalMeta[normalizedExternal] = { build: false };
});
}))
.then(function() {
if (externalMeta)
builder.config({
meta: externalMeta
});
});
}
function canonicalizeConditions(loader, traceOpts) {
return Promise.resolve(Trace.toCanonicalConditionalEnv(loader, traceOpts.conditions))
.then(function(canonicalConditionalEnv) {
traceOpts.conditions = canonicalConditionalEnv;
return Trace.toCanonicalConditionalEnv(loader, traceOpts.inlineConditions);
})
.then(function(canonicalInlineConditionalEnv) {
traceOpts.inlineConditions = canonicalInlineConditionalEnv;
return traceOpts;
});
}
// bundle
Builder.prototype.bundle = function(expressionOrTree, outFile, opts) {
if (outFile && typeof outFile === 'object') {
opts = outFile;
outFile = undefined;
}
var self = this;
if (opts && opts.config)
this.config(opts.config);
var outputOpts = processOutputOpts(opts, { outFile: outFile });
// by default we bundle for the browser in production
var traceOpts = processTraceOpts(opts, { tracePackageConfig: true, browser: true, node: false, production: true, dev: false });
var compileOpts = processCompileOpts(opts);
// override the fetch function if given
if (opts && opts.fetch)
this.fetch = opts.fetch;
return Promise.resolve()
.then(function() {
return canonicalizeConditions(self.loader, traceOpts);
})
.then(function() {
if (typeof expressionOrTree != 'string' && !(expressionOrTree instanceof Array))
return expressionOrTree;
return setExternals(self, traceOpts.externals)
.then(function() {
return traceExpression(self, expressionOrTree, traceOpts);
});
})
.then(function(tree) {
return Promise.resolve()
.then(function() {
if (compileOpts.inlineConditions)
return self.tracer.inlineConditions(tree, self.loader, traceOpts);
return tree;
})
.then(function(tree) {
return compileTree(self.loader, tree, traceOpts, compileOpts, outputOpts, self.cache.compile)
.then(function(compiled) {
return writeOutputs(compiled.outputs, self.loader.baseURL, outputOpts)
.then(function(output) {
output.modules = compiled.modules;
output.entryPoints = compiled.entryPoints;
output.tree = tree;
output.assetList = compiled.assetList;
if (outputOpts.outFile) {
try {
output.bundleName = self.getCanonicalName(toFileURL(path.resolve(outputOpts.outFile)));
}
catch(e) {}
}
return output;
});
});
});
});
};
// build into an optimized static module
Builder.prototype.buildStatic = function(expressionOrTree, outFile, opts) {
if (outFile && typeof outFile === 'object') {
opts = outFile;
outFile = undefined;
}
var self = this;
if (opts && opts.config)
this.config(opts.config);
// NB this "first module" detection should really be done at the arithmetic level
// if only one module is provided, it is an entry point
var entryPoints;
if (typeof expressionOrTree == 'string')
entryPoints = [expressionOrTree.split(/ [\+\&\-] /)[0]];
else if (expressionOrTree instanceof Array)
entryPoints = expressionOrTree[0];
else
entryPoints = [];
// ensure globs are not themsleves entry points
if (entryPoints[0] && entryPoints[0].indexOf('*') != -1)
entryPoints = [];
var outputOpts = processOutputOpts(opts, { outFile: outFile });
// by default we build for production, but not necessarily the browser
var traceOpts = processTraceOpts(opts, { tracePackageConfig: false, production: true, dev: false });
var compileOpts = processCompileOpts(opts, { static: true, entryPoints: entryPoints, encodeNames: true });
var inlineMap;
// override the fetch function if given
if (opts && opts.fetch)
this.fetch = opts.fetch;
return Promise.resolve()
.then(function() {
return canonicalizeConditions(self.loader, traceOpts);
})
.then(function() {
if (typeof expressionOrTree != 'string' && !(expressionOrTree instanceof Array))
return expressionOrTree;
return setExternals(self, traceOpts.externals)
.then(function() {
return traceExpression(self, expressionOrTree, traceOpts);
});
})
.then(function(tree) {
// inline conditionals of the trace
return self.tracer.inlineConditions(tree, self.loader, traceOpts)
.then(function(inlinedTree) {
if (!compileOpts.rollup)
return compileTree(self.loader, inlinedTree, traceOpts, compileOpts, outputOpts, self.cache.compile);
var rollupTree = require('./rollup').rollupTree;
// attempt rollup optimizations of ESM entry points
return rollupTree(self.loader, inlinedTree, [], traceOpts, compileOpts, outputOpts)
.then(function(rolledUp) {
inlineMap = rolledUp.inlineMap;
// we rolled up parts of the tree
if (rolledUp.tree)
return compileTree(self.loader, rolledUp.tree, traceOpts, compileOpts, outputOpts, self.cache.compile);
// if we rolled up the whole tree, then we can skip compile and sfx wrapping entirely
else if (rolledUp.outputs)
return {
outputs: (compileOpts.formatHint ? [getFormatHint(compileOpts)] : []).concat(rolledUp.outputs),
assetList: rolledUp.assetList
};
})
.then(function(compiled) {
return {
modules: Object.keys(tree).filter(function(m) {
return tree[m] && !tree[m].conditional;
}),
assetList: compiled.assetList || [],
outputs: compiled.outputs
};
})
})
.then(function(compiled) {
return writeOutputs(compiled.outputs, self.loader.baseURL, outputOpts)
.then(function(output) {
if (inlineMap)
output.inlineMap = inlineMap;
output.assetList = compiled.assetList;
output.modules = compiled.modules;
output.tree = tree;
return output;
});
});
});
};
Builder.prototype.build = function() {
console.warn('builder.build is deprecated. Using builder.bundle instead.');
return this.bundle.apply(this, arguments);
};
Builder.prototype.buildSFX = function() {
console.warn('builder.buildSFX is deprecated. Using builder.buildStatic instead.');
return this.buildStatic.apply(this, arguments);
};
Builder.prototype.buildTree = function() {
console.warn('builder.buildTree is deprecated. Using builder.bundle instead, which takes both a tree object or expression string.');
return this.bundle.apply(this, arguments);
};
// given a tree, creates a depCache for it
Builder.prototype.getDepCache = function(tree) {
var depCache = {};
Object.keys(tree).forEach(function(moduleName) {
var load = tree[moduleName];
if (load && load.deps && load.deps.length)
depCache[moduleName] = load.deps.map(function(dep) {
return dep;
});
});
return depCache;
};
Builder.prototype.getDeferredImports = function(tree) {
var getDeferredImports = require('./get-deferred-imports');
return getDeferredImports(this, tree);
}
// expose useful tree statics on the builder instance for ease-of-use
Builder.prototype.intersectTrees = require('./arithmetic').intersectTrees;
Builder.prototype.addTrees = require('./arithmetic').addTrees;
Builder.prototype.subtractTrees = require('./arithmetic').subtractTrees;
module.exports = Builder;