UNPKG

27.9 kBJavaScriptView Raw
1var Promise = require('bluebird');
2
3var System = require('systemjs');
4
5var asp = require('bluebird').promisify;
6var fs = require('fs');
7var path = require('path');
8
9var extend = require('./utils').extend;
10
11var attachCompilers = require('./compile').attachCompilers;
12var compileTree = require('./compile').compileTree;
13var compileLoad = require('./compile').compileLoad;
14var pluginBundleHook = require('./compile').pluginBundleHook;
15var writeOutputs = require('./output').writeOutputs;
16
17var traceExpression = require('./arithmetic').traceExpression;
18
19var Trace = require('./trace');
20
21var getCanonicalName = require('./utils').getCanonicalName;
22
23var fromFileURL = require('./utils').fromFileURL;
24var toFileURL = require('./utils').toFileURL;
25
26var createHash = require('crypto').createHash;
27var getFormatHint = require('./utils').getFormatHint;
28
29// new Builder(baseURL)
30// new Builder({cfg})
31// new Builder(baseURL, {cfg})
32// new Builder(baseURL, 'cfg-file.js')
33//
34// baseURL is ignored in cfg when given
35// cfg is saved and used by reset
36function Builder(baseURL, cfg) {
37 if (typeof baseURL == 'object') {
38 cfg = baseURL;
39 baseURL = null;
40 }
41
42 this.loader = null;
43 this.resetConfig = function() {};
44
45 this.reset();
46
47 if (baseURL)
48 this.config({ baseURL: baseURL });
49
50 // config passed to constructor will
51 // be saved for future .reset() calls
52 if (typeof cfg == 'object')
53 this.config(cfg, true, !!baseURL);
54 else if (typeof cfg == 'string')
55 this.loadConfigSync(cfg, true, !!baseURL);
56}
57
58// invalidate the cache for a given module name
59// accepts wildcards (*) which are taken to be deep-matching
60Builder.prototype.invalidate = function(invalidationModuleName) {
61 var loader = this.loader;
62
63 var invalidated = [];
64
65 var self = this;
66
67 // invalidation happens in normalized-space
68 invalidationModuleName = loader.decanonicalize(invalidationModuleName);
69
70 // wildcard detection and handling
71 var invalidationWildcardIndex = invalidationModuleName.indexOf('*');
72 if (invalidationModuleName.lastIndexOf('*') != invalidationWildcardIndex)
73 throw new TypeError('Only a single wildcard supported for invalidation.');
74
75 if (invalidationWildcardIndex != -1) {
76 var wildcardLHS = invalidationModuleName.substr(0, invalidationWildcardIndex);
77 var wildcardRHS = invalidationModuleName.substr(invalidationWildcardIndex + 1);
78 }
79
80 function matchesInvalidation(moduleName) {
81 if (moduleName == invalidationModuleName)
82 return true;
83
84 if (invalidationWildcardIndex == -1)
85 return false;
86
87 return moduleName.substr(0, invalidationWildcardIndex) == wildcardLHS
88 && moduleName.substr(moduleName.length - wildcardRHS.length) == wildcardRHS;
89 }
90
91 // invalidate the given path in the trace cache
92 var traceCache = this.cache.trace;
93 Object.keys(traceCache).forEach(function(canonical) {
94 var moduleName = loader.decanonicalize(canonical);
95 if (matchesInvalidation(moduleName)) {
96 if (traceCache[canonical])
97 invalidated.push(moduleName);
98 traceCache[canonical] = undefined;
99 }
100 });
101
102 // clear the given path from the pluginLoader registry
103 var pluginLoader = loader.pluginLoader;
104 Object.keys(pluginLoader._loader.modules).forEach(function(moduleName) {
105 if (matchesInvalidation(moduleName)) {
106 invalidated.push(moduleName);
107 pluginLoader.delete(moduleName);
108 }
109 });
110
111 // clear the loader define cache
112 loader.defined = {};
113
114 // we leave the compile cache in-tact as it is hashed
115
116 // return array of invalidated canonicals
117 return invalidated;
118};
119
120Builder.prototype.reset = function(baseLoader) {
121 baseLoader = baseLoader || this.loader || System;
122
123 var loader = this.loader = new baseLoader.constructor();
124 // make builder accessible in plugins
125 loader.builder = this;
126 // put the loader in the builder environment
127 loader.config({ build: true });
128
129 loader.import = function(name) {
130 return Promise.reject(new Error('Unable to import "' + name + '".\nThe incorrect instance of System is being used to System.import.'));
131 };
132
133 // ensure this loader doesn't attempt to load the runtime
134 loader._loader.loadedTranspilerRuntime = true;
135
136 var pluginLoader = loader.pluginLoader = new baseLoader.constructor();
137 pluginLoader.config({ build: true });
138
139 loader.constructor = pluginLoader.constructor = baseLoader.constructor;
140 loader.baseURL = pluginLoader.baseURL = baseLoader.baseURL;
141
142 loader.normalize = pluginLoader.normalize = baseLoader.normalize;
143 loader.normalizeSync = pluginLoader.normalizeSync = baseLoader.normalizeSync;
144
145 // store original hooks for next reset
146 loader.originalHooks = baseLoader.originalHooks || {
147 locate: baseLoader.locate,
148 fetch: baseLoader.fetch,
149 translate: baseLoader.translate,
150 instantiate: baseLoader.instantiate
151 };
152
153 loader.locate = pluginLoader.locate = loader.originalHooks.locate;
154 loader.fetch = pluginLoader.fetch = loader.originalHooks.fetch;
155 loader.translate = pluginLoader.translate = loader.originalHooks.translate;
156 loader.instantiate = pluginLoader.instantiate = loader.originalHooks.instantiate;
157
158 var loaderConfig = loader.config;
159 loader.config = function(cfg) {
160 // plugin loader is dev, Node environment
161 loader.pluginLoader.config(cfg);
162 var lCfg = extend({}, cfg);
163 loaderConfig.call(this, lCfg);
164 loader.configHash = generateConfigHash(loader);
165 };
166
167 this.getCanonicalName = getCanonicalName.bind(null, this.loader);
168 this.loader.getCanonicalName = this.loader.pluginLoader.getCanonicalName = this.getCanonicalName;
169
170 attachCompilers(loader);
171
172 var builder = this;
173
174 // allow a custom fetch hook
175 var loaderFetch = loader.fetch;
176 var cachedFetch = function(load) {
177 return Promise.resolve(loaderFetch.call(this, load))
178 .then(function(source) {
179 // calling default fs.readFile fetch -> set timestamp as well for cache invalidation
180 return asp(fs.stat)(fromFileURL(load.address))
181 .then(function(stats) {
182 load.metadata.timestamp = stats.mtime.getTime();
183 return source;
184 }, function(err) {
185 // if the stat fails on a plugin, it may not be linked to the file itself
186 if (err.code == 'ENOENT' && load.metadata.loader)
187 return source;
188 throw err;
189 });
190 });
191 };
192
193 loader.fetch = function(load) {
194 if (builder.fetch)
195 return Promise.resolve(builder.fetch.call(this, load, cachedFetch.bind(this)));
196 else
197 return cachedFetch.call(this, load);
198 };
199
200 // this allows us to normalize package conditionals into package conditionals
201 // package environment normalization handling for fallbacks,
202 // which dont resolve in the loader itself unlike other conditionals which
203 // have this condition inlined under loader.builder in conditionals.js
204 var loaderNormalize = loader.normalize;
205 loader.normalize = function(name, parentName, parentAddress) {
206 var pkgConditional;
207 var pkgConditionalIndex = name.indexOf('/#:');
208 if (pkgConditionalIndex != -1) {
209 pkgConditional = name.substr(pkgConditionalIndex);
210 name = name.substr(0, pkgConditionalIndex + 1);
211 }
212 return loaderNormalize.call(this, name, parentName, parentAddress)
213 .then(function(normalized) {
214 if (pkgConditional)
215 normalized = normalized + pkgConditional;
216 return normalized;
217 });
218 };
219
220 var loaderNormalizeSync = loader.normalizeSync;
221 loader.normalizeSync = function(name, parentName, parentAddress) {
222 var pkgConditional;
223 var pkgConditionalIndex = name.indexOf('#:');
224 if (pkgConditionalIndex != -1) {
225 pkgConditional = name.substr(pkgConditionalIndex);
226 name = name.substr(0, pkgConditionalIndex) + '/';
227 }
228 var normalized = loaderNormalizeSync.call(this, name, parentName, parentAddress);
229 if (pkgConditional)
230 normalized = normalized.substr(0, normalized.length - 1) + pkgConditional;
231 return normalized;
232 };
233
234 // run saved configuration functions against new loader instance
235 this.resetConfig();
236
237 // create cache if not existing
238 this.setCache(this.cache || {});
239
240 // mark all traces as unfresh
241 var traceCache = this.cache.trace;
242 Object.keys(traceCache).forEach(function(canonical) {
243 if (traceCache[canonical])
244 traceCache[canonical].fresh = false;
245 });
246};
247
248function generateConfigHash(loader) {
249 return createHash('md5')
250 .update(JSON.stringify({
251 paths: loader.paths,
252 packages: loader.packages,
253 meta: loader.meta,
254 map: loader.map
255 }))
256 .digest('hex');
257}
258
259Builder.prototype.setCache = function(cacheObj) {
260 this.cache = {
261 compile: cacheObj.compile || {},
262 trace: cacheObj.trace || {}
263 };
264 this.cache.compile.encodings = this.cache.compile.encodings || {};
265 this.cache.compile.loads = this.cache.compile.loads || {};
266 this.tracer = new Trace(this.loader, this.cache.trace);
267};
268
269Builder.prototype.getCache = function() {
270 return this.cache;
271};
272
273Builder.prototype.clearCache = function() {
274 this.setCache({});
275};
276
277function executeConfigFile(saveForReset, ignoreBaseURL, configPath, source) {
278 var self = this;
279
280 // create a safe loader to give to the config execution
281 var configLoader = Object.create(this.loader);
282 configLoader.config = function(cfg) {
283 self.config(cfg, saveForReset, ignoreBaseURL);
284 };
285
286 // make everything in global available to config file code
287 // only substitute local copy of System
288 // and prevent that code from adding anything to the real global
289 var context = Object.create(global);
290 context.SystemJS = context.System = configLoader;
291 context.global = context;
292 if (process.versions.node && process.versions.node.split('.')[0] < 6)
293 context.GLOBAL = context.root = context;
294 // make require available too, make it look as if config file was
295 // loaded like a module
296 var Module = require('module');
297 var m = new Module(configPath);
298 m.filename = configPath;
299 m.paths = Module._nodeModulePaths(path.dirname(configPath));
300 context.require = m.require.bind(m);
301 require('vm').runInNewContext(source.toString(), context);
302}
303
304Builder.prototype.loadConfig = function(configFile, saveForReset, ignoreBaseURL) {
305 return asp(fs.readFile)(configFile)
306 .then(executeConfigFile.bind(this, saveForReset, ignoreBaseURL, configFile));
307};
308
309Builder.prototype.loadConfigSync = function(configFile, saveForReset, ignoreBaseURL) {
310 var source = fs.readFileSync(configFile);
311 executeConfigFile.call(this, saveForReset, ignoreBaseURL, configFile, source);
312};
313
314// note ignore argument is part of API
315Builder.prototype.config = function(config, saveForReset, ignoreBaseURL) {
316 var cfg = {};
317 for (var p in config) {
318 if (ignoreBaseURL && p == 'baseURL' || p == 'bundles' || p == 'depCache')
319 continue;
320 cfg[p] = config[p];
321 }
322 this.loader.config(cfg);
323 if (saveForReset) {
324 // multiple config calls can be saved for reset
325 var curReset = this.resetConfig;
326 this.resetConfig = function() {
327 curReset.call(this);
328 this.loader.config(cfg);
329 };
330 }
331};
332
333/*
334 * Builder Operations
335 */
336function processTraceOpts(options, defaults) {
337 var opts = {
338 // indicate to plugins to output ES modules instead of System.register for Rollup support
339 outputESM: true,
340 sourceMaps: false,
341
342 // conditional tracing options
343 browser: undefined,
344 node: undefined,
345 production: undefined,
346 dev: undefined,
347 conditions: {
348 '@system-env|default': true,
349 '@system-env|~default': false
350 },
351 inlineConditions: {},
352 traceRuntimePlugin: true,
353
354 // package config tracing (used for bundles only)
355 tracePackageConfig: false,
356
357 // when set, automatically excludes non-file URLs that don't resolve to canonical names
358 // instead of showing the "Unable to calculate canonical name" error
359 excludeURLs: true,
360 externals: []
361 };
362
363 // ensure user environment overrides default environment
364 if (typeof options == 'object') {
365 if (typeof options.browser == 'boolean' || typeof options.node == 'boolean') {
366 // browser true/false -> node is opposite
367 if (typeof options.browser == 'boolean' && typeof options.node != 'boolean')
368 options.node = !options.browser;
369 // node true/false -> browser is opposite
370 else if (typeof options.node == 'boolean' && typeof options.browser != 'boolean')
371 options.browser = !options.node;
372 }
373
374 // development -> dev backwards compat
375 if ('development' in options)
376 options.dev = options.development;
377 if (typeof options.dev == 'boolean' || typeof options.production == 'boolean') {
378 if (typeof options.production != 'boolean')
379 options.production = !options.dev;
380 if (typeof options.dev != 'boolean')
381 options.dev = !options.production;
382 }
383 }
384
385 extend(opts, defaults);
386 extend(opts, options);
387
388 // globalDeps are externals
389 if (opts.globalDeps)
390 opts.externals = opts.externals.concat(Object.keys(opts.globalDeps));
391
392 // conditional tracing defaults
393 if (typeof opts.browser == 'boolean') {
394 // browser, node -> browser, ~browser, node, ~node
395 // !browser, node -> ~browser, node
396 // browser, !node -> browser, ~node
397 // !browser, !node -> ~browser, ~node
398 opts.conditions['@system-env|browser'] = opts.browser;
399 opts.conditions['@system-env|~browser'] = opts.browser === true ? opts.node : !opts.browser;
400 opts.conditions['@system-env|node'] = opts.node;
401 opts.conditions['@system-env|~node'] = opts.node === true ? opts.browser : !opts.node;
402 }
403 if (typeof opts.production == 'boolean') {
404 opts.conditions['@system-env|production'] = opts.production;
405 opts.conditions['@system-env|~production'] = opts.dev;
406 opts.conditions['@system-env|dev'] = opts.dev;
407 opts.conditions['@system-env|~dev'] = opts.production;
408 }
409
410 if (typeof opts.inlineConditions == 'object')
411 extend(opts.conditions, opts.inlineConditions);
412 else if (opts.inlineConditions === true)
413 opts.inlineConditions = opts.conditions;
414 else
415 opts.inlineConditions = {};
416
417 return opts;
418}
419
420Builder.prototype.trace = function(expression, opts) {
421 if (opts && opts.config)
422 this.config(opts.config);
423
424 var traceOpts = processTraceOpts(opts);
425 var self = this;
426 return canonicalizeConditions(self.loader, traceOpts)
427 .then(function() {
428 return setExternals(this, traceOpts.externals);
429 })
430 .then(function() {
431 return traceExpression(self, expression, traceOpts);
432 });
433};
434
435Builder.prototype.traceConditionalEnv = function(expression, opts) {
436 var traceOpts = processTraceOpts(opts);
437
438 var self = this;
439
440 return canonicalizeConditions(self.loader, traceOpts)
441 .then(function() {
442 return self.trace(expression, traceOpts);
443 })
444 .then(function(tree) {
445 return Trace.getConditionalEnv(self, tree, traceOpts);
446 });
447}
448
449function processCompileOpts(options, defaults) {
450 // NB deprecate these warnings
451 // all of the below features were undocumented and experimental, so deprecation needn't be long
452 options = options || {};
453 if ('sfxFormat' in options) {
454 console.warn('SystemJS Builder "sfxFormat" is deprecated and has been renamed to "format".');
455 options.format = options.sfxFormat;
456 }
457 if ('sfxEncoding' in options) {
458 console.warn('SystemJS Builder "sfxEncoding" is deprecated and has been renamed to "encodeNames".');
459 options.encodeNames = sfxEncoding;
460 }
461 if ('sfxGlobals' in options) {
462 console.warn('SystemJS Builder "sfxGlobals" is deprecated and has been renamed to "globalDeps".');
463 options.globalDeps = options.sfxGlobals;
464 }
465 if ('sfxGlobalName' in options) {
466 console.warn('SystemJS Builder "sfxGlobalName" is deprecated and has been renamed to "globalName".');
467 options.globalName = options.sfxGlobalName;
468 }
469
470 var opts = {
471 entryPoints: [],
472 normalize: false,
473 anonymous: false,
474
475 systemGlobal: 'System',
476 // whether to inline package configurations into bundles
477 buildConfig: false,
478 inlinePlugins: true,
479 static: false,
480 encodeNames: undefined,
481
482 sourceMaps: false,
483 lowResSourceMaps: false,
484
485 // static build options
486 // it may make sense to split this out into build options
487 // at a later point as static build and bundle compile diverge further
488 runtime: false,
489 format: 'umd',
490 globalDeps: {},
491 globalName: null,
492 exportDefault: false,
493 // conditionalResolutions: {},
494 // can add a special object here that matches condition predicates to direct module names to use for sfx
495
496 // set to true to build in "format amd" style sfx hints for SystemJS loading
497 formatHint: false,
498
499 // this shouldn't strictly be a compile option (its a trace option)
500 // but the cjs compiler needs it to do NODE_ENV optimization
501 production: undefined,
502
503 // use rollup optimizations
504 rollup: true
505 };
506
507 extend(opts, defaults);
508 extend(opts, options);
509
510 if (options.encodeNames && !('encodeNames' in defaults))
511 throw new Error('encodeNames is only supported for buildStatic.');
512
513 if (typeof opts.production != 'boolean')
514 opts.production = !opts.development;
515
516 if (opts.static) {
517 if (opts.encodeNames !== false)
518 opts.encodeNames = true;
519 // include runtime by default if needed
520 if (options.runtime !== false)
521 opts.runtime = true;
522
523 // static builds have a System closure with a dummy name
524 opts.systemGlobal = '$__System';
525
526 // static builds normalize all requires
527 opts.normalize = true;
528 }
529
530 return opts;
531}
532
533Builder.prototype.compile = function(moduleNameOrLoad, outFile, opts) {
534 if (outFile && typeof outFile == 'object') {
535 opts = outFile;
536 outFile = undefined;
537 }
538
539 if (opts && opts.config)
540 this.config(opts.config);
541
542 var self = this;
543
544 var traceOpts = processTraceOpts(opts, { tracePackageConfig: false, browser: true, node: false, production: true, dev: false, outputESM: false });
545
546 return Promise.resolve()
547 .then(function() {
548 return canonicalizeConditions(self.loader, traceOpts);
549 })
550 .then(function() {
551 if (typeof moduleNameOrLoad != 'string')
552 return moduleNameOrLoad;
553 return self.loader.normalize(moduleNameOrLoad)
554 .then(function(moduleName) {
555 if (!opts || opts.cache !== true)
556 self.invalidate(moduleName);
557 return self.tracer.getLoadRecord(getCanonicalName(self.loader, moduleName), traceOpts);
558 });
559 })
560 .then(function(load) {
561 var compileOpts = processCompileOpts(opts, { normalize: false, anonymous: true });
562 var outputOpts = processOutputOpts(opts, { outFile: outFile });
563
564 return Promise.resolve()
565 .then(function() {
566 return compileLoad(self.loader, load, processCompileOpts(opts, { normalize: false, anonymous: true }), self.cache.compile);
567 })
568 .then(function(output) {
569 if (load.metadata.loader)
570 return pluginBundleHook(self.loader, [load], compileOpts, outputOpts)
571 .then(function(bundleOutput) {
572 return [output].concat(bundleOutput.outputs);
573 });
574
575 return [output];
576 })
577 .then(function(outputs) {
578 return writeOutputs(outputs, self.loader.baseURL, outputOpts);
579 });
580 });
581};
582
583function processOutputOpts(options, defaults) {
584 var opts = {
585 outFile: undefined,
586
587 minify: false,
588 uglify: undefined,
589 mangle: true,
590
591 sourceMaps: false,
592 sourceMapContents: undefined
593 };
594
595 extend(opts, defaults);
596 extend(opts, options);
597
598 opts.uglify = opts.uglify || {};
599 opts.uglify.compress = opts.uglify.compress || {};
600 opts.uglify.beautify = opts.uglify.beautify || {};
601
602 // NB deprecated these for uglify directly
603 if (opts.globalDefs && !('global_defs' in opts.uglify.compress))
604 opts.uglify.compress.global_defs = opts.globalDefs;
605 if (opts.ascii && !('ascii' in opts.uglify.beautify))
606 opts.uglify.beautify.ascii_only = opts.ascii;
607 delete opts.globalDefs;
608 delete opts.ascii;
609
610 if (!('dead_code' in opts.uglify.compress))
611 opts.uglify.compress.dead_code = true;
612 if (!('warnings' in opts.uglify.compress))
613 opts.uglify.compress.warnings = false;
614
615 // source maps 'inline' handling
616 if (opts.sourceMapContents === undefined)
617 opts.sourceMapContents = opts.sourceMaps == 'inline';
618
619 if (opts.sourceMapContents)
620 opts.uglify.sourceMapIncludeSources = true;
621
622 return opts;
623}
624
625function setExternals(builder, externals) {
626 if (!externals)
627 return Promise.resolve();
628 var externalMeta;
629 return Promise.all(externals.map(function(external) {
630 externalMeta = externalMeta || {};
631 return builder.loader.normalize(external)
632 .then(function(normalizedExternal) {
633 externalMeta[normalizedExternal] = { build: false };
634 });
635 }))
636 .then(function() {
637 if (externalMeta)
638 builder.config({
639 meta: externalMeta
640 });
641 });
642}
643
644function canonicalizeConditions(loader, traceOpts) {
645 return Promise.resolve(Trace.toCanonicalConditionalEnv(loader, traceOpts.conditions))
646 .then(function(canonicalConditionalEnv) {
647 traceOpts.conditions = canonicalConditionalEnv;
648
649 return Trace.toCanonicalConditionalEnv(loader, traceOpts.inlineConditions);
650 })
651 .then(function(canonicalInlineConditionalEnv) {
652 traceOpts.inlineConditions = canonicalInlineConditionalEnv;
653
654 return traceOpts;
655 });
656}
657
658// bundle
659Builder.prototype.bundle = function(expressionOrTree, outFile, opts) {
660 if (outFile && typeof outFile === 'object') {
661 opts = outFile;
662 outFile = undefined;
663 }
664
665 var self = this;
666
667 if (opts && opts.config)
668 this.config(opts.config);
669
670 var outputOpts = processOutputOpts(opts, { outFile: outFile });
671 // by default we bundle for the browser in production
672 var traceOpts = processTraceOpts(opts, { tracePackageConfig: true, browser: true, node: false, production: true, dev: false });
673
674 var compileOpts = processCompileOpts(opts);
675
676 // override the fetch function if given
677 if (opts && opts.fetch)
678 this.fetch = opts.fetch;
679
680 return Promise.resolve()
681 .then(function() {
682 return canonicalizeConditions(self.loader, traceOpts);
683 })
684 .then(function() {
685 if (typeof expressionOrTree != 'string' && !(expressionOrTree instanceof Array))
686 return expressionOrTree;
687
688 return setExternals(self, traceOpts.externals)
689 .then(function() {
690 return traceExpression(self, expressionOrTree, traceOpts);
691 });
692 })
693 .then(function(tree) {
694 return Promise.resolve()
695 .then(function() {
696 if (compileOpts.inlineConditions)
697 return self.tracer.inlineConditions(tree, self.loader, traceOpts);
698 return tree;
699 })
700 .then(function(tree) {
701 return compileTree(self.loader, tree, traceOpts, compileOpts, outputOpts, self.cache.compile)
702 .then(function(compiled) {
703 return writeOutputs(compiled.outputs, self.loader.baseURL, outputOpts)
704 .then(function(output) {
705 output.modules = compiled.modules;
706 output.entryPoints = compiled.entryPoints;
707 output.tree = tree;
708 output.assetList = compiled.assetList;
709 if (outputOpts.outFile) {
710 try {
711 output.bundleName = self.getCanonicalName(toFileURL(path.resolve(outputOpts.outFile)));
712 }
713 catch(e) {}
714 }
715 return output;
716 });
717 });
718 });
719 });
720};
721
722// build into an optimized static module
723Builder.prototype.buildStatic = function(expressionOrTree, outFile, opts) {
724 if (outFile && typeof outFile === 'object') {
725 opts = outFile;
726 outFile = undefined;
727 }
728
729 var self = this;
730
731 if (opts && opts.config)
732 this.config(opts.config);
733
734 // NB this "first module" detection should really be done at the arithmetic level
735 // if only one module is provided, it is an entry point
736 var entryPoints;
737 if (typeof expressionOrTree == 'string')
738 entryPoints = [expressionOrTree.split(/ [\+\&\-] /)[0]];
739 else if (expressionOrTree instanceof Array)
740 entryPoints = expressionOrTree[0];
741 else
742 entryPoints = [];
743 // ensure globs are not themsleves entry points
744 if (entryPoints[0] && entryPoints[0].indexOf('*') != -1)
745 entryPoints = [];
746
747 var outputOpts = processOutputOpts(opts, { outFile: outFile });
748 // by default we build for production, but not necessarily the browser
749 var traceOpts = processTraceOpts(opts, { tracePackageConfig: false, production: true, dev: false });
750 var compileOpts = processCompileOpts(opts, { static: true, entryPoints: entryPoints, encodeNames: true });
751 var inlineMap;
752
753 // override the fetch function if given
754 if (opts && opts.fetch)
755 this.fetch = opts.fetch;
756
757 return Promise.resolve()
758 .then(function() {
759 return canonicalizeConditions(self.loader, traceOpts);
760 })
761 .then(function() {
762 if (typeof expressionOrTree != 'string' && !(expressionOrTree instanceof Array))
763 return expressionOrTree;
764
765 return setExternals(self, traceOpts.externals)
766 .then(function() {
767 return traceExpression(self, expressionOrTree, traceOpts);
768 });
769 })
770 .then(function(tree) {
771 // inline conditionals of the trace
772 return self.tracer.inlineConditions(tree, self.loader, traceOpts)
773 .then(function(inlinedTree) {
774 if (!compileOpts.rollup)
775 return compileTree(self.loader, inlinedTree, traceOpts, compileOpts, outputOpts, self.cache.compile);
776
777 var rollupTree = require('./rollup').rollupTree;
778
779 // attempt rollup optimizations of ESM entry points
780 return rollupTree(self.loader, inlinedTree, [], traceOpts, compileOpts, outputOpts)
781 .then(function(rolledUp) {
782 inlineMap = rolledUp.inlineMap;
783
784 // we rolled up parts of the tree
785 if (rolledUp.tree)
786 return compileTree(self.loader, rolledUp.tree, traceOpts, compileOpts, outputOpts, self.cache.compile);
787
788 // if we rolled up the whole tree, then we can skip compile and sfx wrapping entirely
789 else if (rolledUp.outputs)
790 return {
791 outputs: (compileOpts.formatHint ? [getFormatHint(compileOpts)] : []).concat(rolledUp.outputs),
792 assetList: rolledUp.assetList
793 };
794 })
795 .then(function(compiled) {
796 return {
797 modules: Object.keys(tree).filter(function(m) {
798 return tree[m] && !tree[m].conditional;
799 }),
800 assetList: compiled.assetList || [],
801 outputs: compiled.outputs
802 };
803 })
804 })
805 .then(function(compiled) {
806 return writeOutputs(compiled.outputs, self.loader.baseURL, outputOpts)
807 .then(function(output) {
808 if (inlineMap)
809 output.inlineMap = inlineMap;
810 output.assetList = compiled.assetList;
811 output.modules = compiled.modules;
812 output.tree = tree;
813 return output;
814 });
815 });
816 });
817};
818
819Builder.prototype.build = function() {
820 console.warn('builder.build is deprecated. Using builder.bundle instead.');
821 return this.bundle.apply(this, arguments);
822};
823Builder.prototype.buildSFX = function() {
824 console.warn('builder.buildSFX is deprecated. Using builder.buildStatic instead.');
825 return this.buildStatic.apply(this, arguments);
826};
827Builder.prototype.buildTree = function() {
828 console.warn('builder.buildTree is deprecated. Using builder.bundle instead, which takes both a tree object or expression string.');
829 return this.bundle.apply(this, arguments);
830};
831
832// given a tree, creates a depCache for it
833Builder.prototype.getDepCache = function(tree) {
834 var depCache = {};
835 Object.keys(tree).forEach(function(moduleName) {
836 var load = tree[moduleName];
837 if (load && load.deps && load.deps.length)
838 depCache[moduleName] = load.deps.map(function(dep) {
839 return dep;
840 });
841 });
842 return depCache;
843};
844
845Builder.prototype.getDeferredImports = function(tree) {
846 var getDeferredImports = require('./get-deferred-imports');
847 return getDeferredImports(this, tree);
848}
849
850// expose useful tree statics on the builder instance for ease-of-use
851Builder.prototype.intersectTrees = require('./arithmetic').intersectTrees;
852Builder.prototype.addTrees = require('./arithmetic').addTrees;
853Builder.prototype.subtractTrees = require('./arithmetic').subtractTrees;
854
855module.exports = Builder;