1 | var getCanonicalName = require('./utils').getCanonicalName;
|
2 | var glob = require('glob');
|
3 | var toFileURL = require('./utils').toFileURL;
|
4 | var fromFileURL = require('./utils').fromFileURL;
|
5 | var asp = require('bluebird').promisify;
|
6 | var fs = require('fs');
|
7 | var path = require('path');
|
8 | var extend = require('./utils').extend;
|
9 | var Promise = require('bluebird');
|
10 | var getPackage = require('./utils').getPackage;
|
11 | var getPackageConfigPath = require('./utils').getPackageConfigPath;
|
12 | var isPackageConfig = require('./utils').isPackageConfig;
|
13 | var parseCondition = require('./utils').parseCondition;
|
14 | var serializeCondition = require('./utils').serializeCondition;
|
15 | var dataUriToBuffer = require('data-uri-to-buffer');
|
16 |
|
17 | module.exports = Trace;
|
18 |
|
19 | function Trace(loader, traceCache) {
|
20 |
|
21 | Object.keys(traceCache).forEach(function(canonical) {
|
22 | var load = traceCache[canonical];
|
23 |
|
24 | if (load && !load.conditional)
|
25 | load.fresh = false;
|
26 | });
|
27 |
|
28 |
|
29 | this.loader = loader;
|
30 |
|
31 | this.loads = traceCache || {};
|
32 |
|
33 | this.tracing = {};
|
34 | }
|
35 |
|
36 |
|
37 |
|
38 |
|
39 | var namedRegisterRegEx = /(System\.register(Dynamic)?|define)\(('[^']+'|"[^"]+")/g;
|
40 | Trace.prototype.traceModule = function(moduleName, traceOpts) {
|
41 | var loader = this.loader;
|
42 |
|
43 | return Promise.resolve(loader.normalize(moduleName))
|
44 | .then(function(normalized) {
|
45 | return traceCanonical(getCanonicalName(loader, normalized), traceOpts);
|
46 | });
|
47 | };
|
48 |
|
49 |
|
50 | Trace.getConditionalEnv = function(builder, loads, traceOpts) {
|
51 | var conditionalEnvVariations = {};
|
52 |
|
53 | Object.keys(loads).forEach(function(canonical) {
|
54 | var load = loads[canonical];
|
55 | if (!load.conditional)
|
56 | return;
|
57 |
|
58 | var envVariations = Trace.getConditionalResolutions(load.conditional, traceOpts.conditions, traceOpts.inlineConditions).conditionalEnvVariations;
|
59 |
|
60 | Object.keys(envVariations).forEach(function(condition) {
|
61 | if (envVariations[condition] instanceof Array) {
|
62 | conditionalEnvVariations[condition] = conditionalEnvVariations[condition] || [];
|
63 | envVariations[condition].forEach(function(conditionValue) {
|
64 | if (conditionalEnvVariations[condition].indexOf(conditionValue) === -1)
|
65 | conditionalEnvVariations[condition].push(conditionValue);
|
66 | });
|
67 | }
|
68 | else {
|
69 | conditionalEnvVariations[condition] = conditionalEnvVariations[condition] || envVariations[condition];
|
70 | }
|
71 | });
|
72 | });
|
73 | return conditionalEnvVariations;
|
74 | };
|
75 |
|
76 | Trace.prototype.traceCanonical = function(canonical, traceOpts) {
|
77 | var self = this;
|
78 |
|
79 | return self.getAllLoadRecords(canonical, traceOpts, traceOpts.conditions, traceOpts.inlineConditions, {}, [])
|
80 | .then(function(loads) {
|
81 |
|
82 |
|
83 | var thisLoad = loads[canonical];
|
84 |
|
85 | if (thisLoad && !thisLoad.conditional && thisLoad.metadata.bundle) {
|
86 | namedRegisterRegEx.lastIndex = 0;
|
87 | var curMatch;
|
88 | while ((curMatch = namedRegisterRegEx.exec(thisLoad.source)))
|
89 | loads[curMatch[3].substr(1, curMatch[3].length - 2)] = true;
|
90 | }
|
91 |
|
92 | return {
|
93 | moduleName: canonical,
|
94 | tree: loads
|
95 | };
|
96 | });
|
97 | }
|
98 |
|
99 | function isLoadFresh(load, loader, loads) {
|
100 | if (load === undefined)
|
101 | return false;
|
102 |
|
103 | if (load === false)
|
104 | return true;
|
105 |
|
106 | if (load.configHash != loader.configHash)
|
107 | return false;
|
108 |
|
109 | if (load.fresh)
|
110 | return true;
|
111 |
|
112 | if (load.conditional)
|
113 | return false;
|
114 |
|
115 | if (load.plugin) {
|
116 | if (!loader.pluginLoader.has(loader.pluginLoader.decanonicalize(load.plugin)))
|
117 | return false;
|
118 | }
|
119 |
|
120 |
|
121 | try {
|
122 | var timestamp = fs.statSync(path.resolve(fromFileURL(loader.baseURL), load.path)).mtime.getTime();
|
123 | }
|
124 | catch(e) {}
|
125 | return load.fresh = timestamp == load.timestamp;
|
126 | }
|
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 | Trace.prototype.getLoadRecord = function(canonical, traceOpts, parentStack) {
|
133 | var loader = this.loader;
|
134 | var loads = this.loads;
|
135 |
|
136 | if (isLoadFresh(loads[canonical], loader, loads))
|
137 | return Promise.resolve(loads[canonical]);
|
138 |
|
139 | if (this.tracing[canonical])
|
140 | return this.tracing[canonical];
|
141 |
|
142 | var self = this;
|
143 | var isPackageConditional = canonical.indexOf('/#:') != -1;
|
144 |
|
145 | var curMap = loader.map;
|
146 | loader.map = {};
|
147 | var normalized = loader.decanonicalize(canonical);
|
148 | loader.map = curMap;
|
149 |
|
150 | return this.tracing[canonical] = Promise.resolve(normalized)
|
151 | .then(function(normalized) {
|
152 |
|
153 | if (loader.has(normalized))
|
154 | return false;
|
155 |
|
156 |
|
157 | if (!isPackageConditional)
|
158 | normalized = normalized.replace('/#:', '/');
|
159 |
|
160 |
|
161 |
|
162 |
|
163 | var booleanIndex = canonical.lastIndexOf('#?');
|
164 | if (booleanIndex != -1) {
|
165 | var condition = canonical.substr(booleanIndex + 2);
|
166 | if (condition.indexOf('|') == -1)
|
167 | condition += '|default';
|
168 | return {
|
169 | name: canonical,
|
170 | fresh: true,
|
171 | conditional: {
|
172 | condition: condition,
|
173 | branch: canonical.substr(0, booleanIndex)
|
174 | }
|
175 | };
|
176 | }
|
177 |
|
178 |
|
179 | var pkgEnvIndex = canonical.indexOf('/#:');
|
180 | if (pkgEnvIndex != -1) {
|
181 |
|
182 | if (canonical.indexOf('!') != -1)
|
183 | throw new Error('Unable to trace ' + canonical + ' - building package environment mappings of plugins is not currently supported.');
|
184 |
|
185 | var pkgName = canonical.substr(0, pkgEnvIndex);
|
186 | var subPath = canonical.substr(pkgEnvIndex + 3);
|
187 |
|
188 | var normalizedPkgName = loader.decanonicalize(pkgName);
|
189 |
|
190 |
|
191 | var loadPackageConfig;
|
192 | var packageConfigPath = getPackageConfigPath(loader.packageConfigPaths, normalizedPkgName);
|
193 | if (packageConfigPath) {
|
194 | loadPackageConfig = getCanonicalName(loader, packageConfigPath);
|
195 | (loader.meta[packageConfigPath] = loader.meta[packageConfigPath] || {}).format = 'json';
|
196 | }
|
197 |
|
198 |
|
199 |
|
200 |
|
201 |
|
202 | var absURLRegEx = /^[^\/]+:\/\//;
|
203 | function isPlain(name) {
|
204 | return name[0] != '.' && name[0] != '/' && !name.match(absURLRegEx);
|
205 | }
|
206 | function getMapMatch(map, name) {
|
207 | var bestMatch, bestMatchLength = 0;
|
208 |
|
209 | for (var p in map) {
|
210 | if (name.substr(0, p.length) == p && (name.length == p.length || name[p.length] == '/')) {
|
211 | var curMatchLength = p.split('/').length;
|
212 | if (curMatchLength <= bestMatchLength)
|
213 | continue;
|
214 | bestMatch = p;
|
215 | bestMatchLength = curMatchLength;
|
216 | }
|
217 | }
|
218 |
|
219 | return bestMatch;
|
220 | }
|
221 | function toPackagePath(subPath, isFallback) {
|
222 | if (isPlain(subPath)) {
|
223 |
|
224 | if (!isFallback)
|
225 | return loader.normalize(subPath, normalizedPkgName + '/');
|
226 |
|
227 | else
|
228 | return loader.normalize(subPath);
|
229 | }
|
230 | else if (subPath == '.') {
|
231 | return Promise.resolve(normalizedPkgName);
|
232 | }
|
233 | else if (subPath.substr(0, 2) == './') {
|
234 | var pkgMap = pkg.map;
|
235 | pkg.map = {};
|
236 | var normalized = loader.normalizeSync(pkgName + '/' + subPath.substr(2));
|
237 | pkg.map = pkgMap;
|
238 | return Promise.resolve(normalized);
|
239 | }
|
240 | else {
|
241 | return Promise.resolve(normalized);
|
242 | }
|
243 | }
|
244 |
|
245 | var pkg
|
246 | var envMap;
|
247 | var metadata = {};
|
248 |
|
249 |
|
250 |
|
251 |
|
252 |
|
253 | return loader.normalize(normalizedPkgName)
|
254 | .then(function() {
|
255 | pkg = loader.packages[normalizedPkgName];
|
256 |
|
257 | var mapMatch = getMapMatch(pkg.map, subPath);
|
258 | envMap = pkg.map[mapMatch];
|
259 |
|
260 | if (!envMap)
|
261 | throw new Error('Package conditional ' + canonical + ' has no package conditional map present.');
|
262 |
|
263 |
|
264 | return toPackagePath(subPath, true);
|
265 | })
|
266 | .then(function(resolvedPath) {
|
267 |
|
268 | if (resolvedPath.match(/\#[\:\?\{]/))
|
269 | return getCanonicalName(loader, resolvedPath);
|
270 |
|
271 |
|
272 | if (isPlain(resolvedPath))
|
273 | return resolvedPath;
|
274 |
|
275 | return loader.locate({ name: resolvedPath, metadata: metadata })
|
276 | .then(function(address) {
|
277 |
|
278 | if (metadata.build === false)
|
279 | return false;
|
280 |
|
281 |
|
282 | return new Promise(function(resolve) {
|
283 | fs.exists(fromFileURL(address), resolve);
|
284 | })
|
285 | .then(function(fallbackExists) {
|
286 | if (fallbackExists)
|
287 | return getCanonicalName(loader, resolvedPath);
|
288 | });
|
289 | });
|
290 | })
|
291 | .then(function(fallback) {
|
292 |
|
293 | return Promise.all(Object.keys(envMap).map(function(envCondition) {
|
294 | var mapping = envMap[envCondition];
|
295 | var conditionObj = parseCondition(envCondition);
|
296 |
|
297 | return loader.normalize(conditionObj.module, normalizedPkgName)
|
298 | .then(function(conditionModule) {
|
299 | conditionObj.module = getCanonicalName(loader, conditionModule);
|
300 | return toPackagePath(mapping, false);
|
301 | })
|
302 | .then(function(normalizedMapping) {
|
303 | return {
|
304 | condition: serializeCondition(conditionObj),
|
305 | branch: getCanonicalName(loader, normalizedMapping)
|
306 | };
|
307 | });
|
308 | }))
|
309 | .then(function(envs) {
|
310 | return {
|
311 | name: canonical,
|
312 | fresh: true,
|
313 | packageConfig: loadPackageConfig,
|
314 | conditional: {
|
315 | envs: envs,
|
316 | fallback: fallback
|
317 | }
|
318 | };
|
319 | });
|
320 | });
|
321 | }
|
322 |
|
323 |
|
324 | var interpolationRegEx = /#\{[^\}]+\}/;
|
325 | var interpolationMatch = canonical.match(interpolationRegEx);
|
326 | if (interpolationMatch) {
|
327 | var condition = interpolationMatch[0].substr(2, interpolationMatch[0].length - 3);
|
328 |
|
329 | if (condition.indexOf('|') == -1)
|
330 | condition += '|default';
|
331 |
|
332 | var metadata = {};
|
333 | return Promise.resolve(loader.locate({ name: normalized.replace(interpolationRegEx, '*'), metadata: metadata }))
|
334 | .then(function(address) {
|
335 | if (address.substr(0, 8) != 'file:///' && !load.metadata.loader)
|
336 | metadata.build = false;
|
337 |
|
338 |
|
339 | if (metadata.build === false)
|
340 | return false;
|
341 |
|
342 |
|
343 | var globIndex = address.indexOf('*');
|
344 | return asp(glob)(fromFileURL(address), { dot: true, nobrace: true, noglobstar: true, noext: true, nodir: true })
|
345 | .then(function(paths) {
|
346 | var branches = {};
|
347 | paths.forEach(function(path) {
|
348 | path = toFileURL(path);
|
349 |
|
350 | var pathCanonical = getCanonicalName(loader, path);
|
351 | var interpolate = pathCanonical.substr(interpolationMatch.index, path.length - address.length + 1);
|
352 |
|
353 | if (normalized.indexOf('!') != -1) {
|
354 | if (loader.pluginFirst)
|
355 | pathCanonical = getCanonicalName(loader, metadata.loader) + '!' + pathCanonical;
|
356 | else
|
357 | pathCanonical = pathCanonical + '!' + getCanonicalName(loader, metadata.loader);
|
358 | }
|
359 | branches[interpolate] = pathCanonical;
|
360 | });
|
361 |
|
362 |
|
363 |
|
364 | if (traceOpts.conditions[condition])
|
365 | traceOpts.conditions[condition].forEach(function(c) {
|
366 | if (branches[c])
|
367 | return;
|
368 | var branchCanonical = canonical.substr(0, interpolationMatch.index) + c + canonical.substr(interpolationMatch[0].length + interpolationMatch.index);
|
369 | branches[c] = branchCanonical;
|
370 | });
|
371 |
|
372 | return {
|
373 | name: canonical,
|
374 | fresh: false,
|
375 | conditional: {
|
376 | condition: condition,
|
377 | branches: branches
|
378 | }
|
379 | };
|
380 | });
|
381 | });
|
382 | }
|
383 |
|
384 |
|
385 | var load = {
|
386 | name: canonical,
|
387 |
|
388 | path: null,
|
389 | metadata: {},
|
390 | deps: [],
|
391 | depMap: {},
|
392 | source: null,
|
393 |
|
394 |
|
395 | fresh: true,
|
396 |
|
397 | timestamp: null,
|
398 |
|
399 |
|
400 | configHash: loader.configHash,
|
401 |
|
402 | plugin: null,
|
403 | runtimePlugin: false,
|
404 |
|
405 |
|
406 | pluginConfig: null,
|
407 |
|
408 |
|
409 | packageConfig: null,
|
410 | isPackageConfig: isPackageConfig(loader, canonical),
|
411 |
|
412 |
|
413 | deferredImports: null,
|
414 |
|
415 |
|
416 | compactedLoads: null,
|
417 | };
|
418 | var curHook = 'locate';
|
419 | var originalSource;
|
420 | return Promise.resolve(loader.locate({ name: normalized, metadata: load.metadata}))
|
421 | .then(function(address) {
|
422 | curHook = '';
|
423 |
|
424 | if (address.substr(0, 8) != 'file:///' && !load.metadata.loader)
|
425 | load.metadata.build = false;
|
426 |
|
427 |
|
428 | if (load.metadata.build === false)
|
429 | return false;
|
430 |
|
431 | if (address.substr(0, 8) == 'file:///')
|
432 | load.path = path.relative(fromFileURL(loader.baseURL), fromFileURL(address));
|
433 |
|
434 | return Promise.resolve()
|
435 | .then(function() {
|
436 |
|
437 | if (load.metadata.loaderModule)
|
438 | return Promise.resolve(loader.pluginLoader.normalize(load.metadata.loader, normalized))
|
439 | .then(function(pluginNormalized) {
|
440 | load.plugin = getCanonicalName(loader, pluginNormalized);
|
441 |
|
442 | if (load.metadata.loaderModule &&
|
443 | (load.metadata.loaderModule.build === false || Object.keys(load.metadata.loaderModule).length == 0 ||
|
444 | (typeof load.metadata.loaderModule === 'function' || load.metadata.loaderModule.toString && load.metadata.loaderModule.toString() === 'Module' && typeof load.metadata.loaderModule.default === 'function')))
|
445 | load.runtimePlugin = true;
|
446 |
|
447 | if (pluginNormalized.indexOf('!') == -1 && !load.runtimePlugin && getPackage(loader.packages, pluginNormalized)) {
|
448 | var packageConfigPath = getPackageConfigPath(loader.packageConfigPaths, pluginNormalized);
|
449 | if (packageConfigPath) {
|
450 | load.pluginConfig = getCanonicalName(loader, packageConfigPath);
|
451 | (loader.meta[packageConfigPath] = loader.meta[packageConfigPath] || {}).format = 'json';
|
452 | }
|
453 | }
|
454 | });
|
455 | })
|
456 | .then(function() {
|
457 | if (load.runtimePlugin)
|
458 | return load;
|
459 |
|
460 | curHook = 'fetch';
|
461 |
|
462 |
|
463 | return loader
|
464 | .fetch({ name: normalized, metadata: load.metadata, address: address })
|
465 |
|
466 |
|
467 | .then(function (source) {
|
468 | if (!traceOpts.sourceMaps || load.metadata.sourceMap)
|
469 | return source;
|
470 |
|
471 |
|
472 | var sourceMap = source.match(/^\s*\/\/\s*[#@] sourceMappingURL=([^\s'"]*)/m);
|
473 |
|
474 | if (!sourceMap)
|
475 | return source;
|
476 |
|
477 |
|
478 | if (sourceMap[1].startsWith('data:')) {
|
479 | load.metadata.sourceMap = JSON.parse(dataUriToBuffer(sourceMap[1]).toString('utf8'));
|
480 | return source;
|
481 | } else {
|
482 |
|
483 |
|
484 | var sourceMapPath = path.join(path.dirname(fromFileURL(address)), sourceMap[1]);
|
485 |
|
486 | return asp(fs.readFile)(sourceMapPath, 'utf8').then(function (sourceMap) {
|
487 | load.metadata.sourceMap = JSON.parse(sourceMap);
|
488 | return source;
|
489 | })
|
490 |
|
491 | .catch(function() {
|
492 | return source;
|
493 | });
|
494 | }
|
495 | })
|
496 |
|
497 | .then(function(source) {
|
498 | if (typeof source != 'string')
|
499 | throw new TypeError('Loader fetch hook did not return a source string');
|
500 | originalSource = source;
|
501 | curHook = 'translate';
|
502 |
|
503 |
|
504 | if (load.metadata.timestamp) {
|
505 | load.timestamp = load.metadata.timestamp;
|
506 | load.metadata.timestamp = undefined;
|
507 | }
|
508 |
|
509 | return loader.translate({ name: normalized, metadata: load.metadata, address: address, source: source }, traceOpts);
|
510 | })
|
511 | .then(function(source) {
|
512 |
|
513 | load.source = source;
|
514 | curHook = 'dependency parsing';
|
515 |
|
516 | return loader.instantiate({ name: normalized, metadata: load.metadata, address: address, source: source });
|
517 | })
|
518 | .then(function(result) {
|
519 |
|
520 |
|
521 | if (load.metadata.build === false)
|
522 | return;
|
523 |
|
524 | curHook = '';
|
525 | if (!result)
|
526 | throw new TypeError('Native ES Module builds not supported. Ensure transpilation is included in the loader pipeline.');
|
527 |
|
528 | load.deps = result.deps;
|
529 |
|
530 |
|
531 | if (load.metadata.format == 'esm' && load.metadata.originalSource)
|
532 | load.source = originalSource;
|
533 |
|
534 |
|
535 | if (getPackage(loader.packages, normalized) && !load.isPackageConfig) {
|
536 | var packageConfigPath = getPackageConfigPath(loader.packageConfigPaths, normalized);
|
537 | if (packageConfigPath) {
|
538 | load.packageConfig = getCanonicalName(loader, packageConfigPath);
|
539 | (loader.meta[packageConfigPath] = loader.meta[packageConfigPath] || {}).format = 'json';
|
540 | }
|
541 | }
|
542 |
|
543 |
|
544 | return Promise.all(result.deps.map(function(dep) {
|
545 | return loader.normalize(dep, normalized, address)
|
546 | .then(function(normalized) {
|
547 | try {
|
548 | load.depMap[dep] = getCanonicalName(loader, normalized);
|
549 | }
|
550 | catch(e) {
|
551 | if (!traceOpts.excludeURLs || normalized.substr(0, 7) == 'file://')
|
552 | throw e;
|
553 | load.depMap[dep] = normalized;
|
554 | }
|
555 | });
|
556 | }));
|
557 | });
|
558 | })
|
559 | .catch(function(err) {
|
560 | var msg = (curHook ? ('Error on ' + curHook + ' for ') : 'Error tracing ') + canonical + ' at ' + normalized;
|
561 |
|
562 | if (parentStack)
|
563 | parentStack.reverse().forEach(function(parent) {
|
564 | msg += '\n\tLoading ' + parent;
|
565 | });
|
566 |
|
567 |
|
568 | var newMsg = msg + '\n\t' + (err.message || err);
|
569 | var newErr = new Error(newMsg, err.fileName, err.lineNumber);
|
570 | newErr.originalErr = err.originalErr || err;
|
571 | newErr.stack = msg + '\n\t' + (err.stack || err);
|
572 | throw newErr;
|
573 | })
|
574 | .then(function() {
|
575 |
|
576 | if (load.metadata.format === 'register')
|
577 | load.metadata.format = 'system';
|
578 |
|
579 |
|
580 | load.metadata.entry = undefined;
|
581 | load.metadata.builderExecute = undefined;
|
582 | load.metadata.parseTree = undefined;
|
583 | load.metadata.ast = undefined;
|
584 |
|
585 | if (load.metadata.build === false)
|
586 | return false;
|
587 |
|
588 | return load;
|
589 | });
|
590 | });
|
591 | })
|
592 | .then(function(load) {
|
593 | self.tracing[canonical] = undefined;
|
594 | return loads[canonical] = load;
|
595 | }).catch(function(err) {
|
596 | self.tracing[canonical] = undefined;
|
597 | throw err;
|
598 | });
|
599 | };
|
600 |
|
601 |
|
602 |
|
603 |
|
604 |
|
605 |
|
606 |
|
607 |
|
608 |
|
609 |
|
610 |
|
611 |
|
612 |
|
613 |
|
614 |
|
615 |
|
616 |
|
617 |
|
618 |
|
619 |
|
620 |
|
621 |
|
622 |
|
623 |
|
624 | var systemModules = ['@empty', '@system-env', '@@cjs-helpers', '@@global-helpers'];
|
625 | Trace.prototype.getAllLoadRecords = function(canonical, traceOpts, canonicalConditionalEnv, canonicalInlineConditionals, curLoads, parentStack) {
|
626 | var loader = this.loader;
|
627 |
|
628 | curLoads = curLoads || {};
|
629 |
|
630 | if (canonical in curLoads)
|
631 | return curLoads;
|
632 |
|
633 | var self = this;
|
634 | return this.getLoadRecord(canonical, traceOpts, parentStack)
|
635 | .then(function(load) {
|
636 |
|
637 |
|
638 | if (canonical in curLoads)
|
639 | return curLoads;
|
640 |
|
641 |
|
642 |
|
643 |
|
644 | if (systemModules.indexOf(canonical) == -1)
|
645 | curLoads[canonical] = load;
|
646 |
|
647 | if (load) {
|
648 | parentStack = parentStack.concat([canonical]);
|
649 | return Promise.all(Trace.getLoadDependencies(load, traceOpts, canonicalConditionalEnv, canonicalInlineConditionals).map(function(dep) {
|
650 | return self.getAllLoadRecords(dep, traceOpts, canonicalConditionalEnv, canonicalInlineConditionals, curLoads, parentStack);
|
651 | }));
|
652 | }
|
653 | })
|
654 | .then(function() {
|
655 | return curLoads;
|
656 | });
|
657 | };
|
658 |
|
659 | function conditionalComplement(condition) {
|
660 | var conditionObj = parseCondition(condition);
|
661 | conditionObj.negate = !conditionObj.negate;
|
662 | return serializeCondition(conditionObj);
|
663 | }
|
664 |
|
665 | Trace.toCanonicalConditionalEnv = toCanonicalConditionalEnv;
|
666 | function toCanonicalConditionalEnv(loader, conditionalEnv) {
|
667 | var canonicalConditionalEnv = {};
|
668 |
|
669 |
|
670 |
|
671 |
|
672 |
|
673 |
|
674 |
|
675 | Object.keys(conditionalEnv).map(function(m) {
|
676 | var conditionObj = parseCondition(m);
|
677 |
|
678 | if (!(conditionalEnv[m] instanceof Array)) {
|
679 | if (!conditionObj.negate) {
|
680 | conditionalEnv[m] = [conditionalEnv[m]];
|
681 | }
|
682 | else {
|
683 | var complement = conditionalComplement(m);
|
684 | if (conditionalEnv[complement]) {
|
685 | if (conditionalEnv[complement] instanceof Array) {
|
686 | if (conditionalEnv[complement].indexOf(!conditionalEnv[m]) === -1)
|
687 | conditionalEnv[complement].push(!conditionalEnv[m]);
|
688 | }
|
689 | else {
|
690 | if (!conditionalEnv[m] !== conditionalEnv[complement])
|
691 | conditionalEnv[conditionalComplement(m)] = [conditionalEnv[conditionalComplement(m)], !conditionalEnv[m]];
|
692 | }
|
693 | }
|
694 | else {
|
695 | conditionalEnv[conditionalComplement(m)] = [!conditionalEnv[m]];
|
696 | }
|
697 | delete conditionalEnv[m];
|
698 | }
|
699 | }
|
700 | });
|
701 |
|
702 | return Promise.all(Object.keys(conditionalEnv).map(function(m) {
|
703 | var conditionObj = parseCondition(m);
|
704 | return loader.normalize(conditionObj.module)
|
705 | .then(function(normalized) {
|
706 | conditionObj.module = getCanonicalName(loader, normalized);
|
707 | var canonicalCondition = serializeCondition(conditionObj);
|
708 | canonicalConditionalEnv[canonicalCondition] = conditionalEnv[m];
|
709 | });
|
710 | }))
|
711 | .then(function() {
|
712 | return canonicalConditionalEnv;
|
713 | });
|
714 | }
|
715 |
|
716 |
|
717 |
|
718 |
|
719 |
|
720 |
|
721 |
|
722 | Trace.prototype.inlineConditions = function(tree, loader, traceOpts) {
|
723 | var self = this;
|
724 |
|
725 | var inconsistencyErrorMsg = 'For static condition inlining only an exact environment resolution can be built, pending https://github.com/systemjs/builder/issues/311.';
|
726 |
|
727 |
|
728 | for (var c in traceOpts.inlineConditions) {
|
729 | if (traceOpts.inlineConditions[c].length > 1)
|
730 | throw new TypeError('Error building condition ' + c + '. ' + inconsistencyErrorMsg);
|
731 | }
|
732 |
|
733 | var conditionalResolutions = {};
|
734 | var importsSystemEnv = false;
|
735 |
|
736 |
|
737 | var allConditionals = [];
|
738 | Object.keys(tree).forEach(function(m) {
|
739 | if (m.match(/#[\:\?\{]/) && allConditionals.indexOf(m) === -1)
|
740 | allConditionals.push(m);
|
741 |
|
742 | if (!tree[m] || tree[m].conditional)
|
743 | return;
|
744 |
|
745 | Object.keys(tree[m].depMap).forEach(function(d) {
|
746 | var dep = tree[m].depMap[d];
|
747 |
|
748 | if (dep.match(/#[\:\?\{]/) && allConditionals.indexOf(dep) === -1)
|
749 | allConditionals.push(dep);
|
750 | });
|
751 | });
|
752 |
|
753 |
|
754 | return Promise.all(allConditionals.map(function(c) {
|
755 | return self.getLoadRecord(c, traceOpts)
|
756 | .then(function(load) {
|
757 | var branches = Trace.getConditionalResolutions(load.conditional, traceOpts.conditions, traceOpts.inlineConditions).branches;
|
758 |
|
759 | if (branches.length === 1)
|
760 | conditionalResolutions[c] = branches[0];
|
761 | });
|
762 | }))
|
763 | .then(function() {
|
764 |
|
765 | Object.keys(conditionalResolutions).forEach(function(c) {
|
766 | var resolution = conditionalResolutions[c];
|
767 |
|
768 | var seen = [];
|
769 | while (conditionalResolutions[resolution] && seen.indexOf(resolution) == -1) {
|
770 | seen.push(resolution);
|
771 | resolution = conditionalResolutions[resolution];
|
772 | conditionalResolutions[c] = resolution;
|
773 | }
|
774 | if (seen.indexOf(resolution) != -1)
|
775 | throw new Error('Circular conditional resolution ' + seen.join(' -> ') + ' -> ' + resolution);
|
776 | });
|
777 |
|
778 |
|
779 |
|
780 | var inlinedTree = {};
|
781 | Object.keys(tree).forEach(function(m) {
|
782 | var load = tree[m];
|
783 |
|
784 | if (typeof load == 'boolean') {
|
785 | inlinedTree[m] = load;
|
786 | return;
|
787 | }
|
788 |
|
789 | if (load.conditional)
|
790 | return;
|
791 |
|
792 | var clonedLoad = extend({}, load);
|
793 | clonedLoad.depMap = {};
|
794 | Object.keys(load.depMap).forEach(function(d) {
|
795 | var normalizedDep = load.depMap[d];
|
796 |
|
797 | normalizedDep = conditionalResolutions[normalizedDep] || normalizedDep;
|
798 |
|
799 | if (normalizedDep == '@system-env')
|
800 | importsSystemEnv = true;
|
801 |
|
802 | clonedLoad.depMap[d] = normalizedDep;
|
803 | });
|
804 |
|
805 | inlinedTree[m] = clonedLoad;
|
806 | });
|
807 |
|
808 |
|
809 |
|
810 |
|
811 | if (importsSystemEnv) {
|
812 | inlinedTree['@system-env'] = {
|
813 | name: '@system-env',
|
814 | path: null,
|
815 | metadata: {
|
816 | format: 'json'
|
817 | },
|
818 | deps: [],
|
819 | depMap: {},
|
820 | source: JSON.stringify({
|
821 | production: traceOpts.inlineConditions['@system-env|production'],
|
822 | browser: traceOpts.inlineConditions['@system-env|browser'],
|
823 | node: traceOpts.inlineConditions['@system-env|node'],
|
824 | dev: traceOpts.inlineConditions['@system-env|dev'],
|
825 | default: true
|
826 | }),
|
827 | fresh: true,
|
828 | timestamp: null,
|
829 | configHash: loader.configHash,
|
830 | };
|
831 | }
|
832 |
|
833 | return inlinedTree;
|
834 | });
|
835 | };
|
836 |
|
837 |
|
838 |
|
839 |
|
840 |
|
841 |
|
842 |
|
843 | Trace.getConditionalResolutions = function(conditional, conditionalEnv, inlineConditionals) {
|
844 |
|
845 | var branches = [];
|
846 | var conditionModules = [];
|
847 | var conditionalEnvVariations = {};
|
848 |
|
849 | function traceCondition(condition, value) {
|
850 | var conditionModule = parseCondition(condition).module;
|
851 |
|
852 | if (inlineConditionals[condition])
|
853 | return inlineConditionals[condition][0] === value;
|
854 |
|
855 | if (conditionalEnv[condition] && conditionalEnv[condition].indexOf(value) === -1)
|
856 | return false;
|
857 |
|
858 | conditionalEnvVariations[condition] = conditionalEnvVariations[condition] || [];
|
859 | if (conditionalEnvVariations[condition].indexOf(value) === -1)
|
860 | conditionalEnvVariations[condition].push(value);
|
861 |
|
862 | if (conditionModules.indexOf(conditionModule) == -1)
|
863 | conditionModules.push(conditionModule);
|
864 |
|
865 | return true;
|
866 | }
|
867 |
|
868 |
|
869 | function booleanEnvTrace(condition) {
|
870 | var conditionObj = parseCondition(condition);
|
871 |
|
872 | if (conditionObj.negate)
|
873 | return traceCondition(conditionalComplement(condition), false);
|
874 | else
|
875 | return traceCondition(condition, true);
|
876 | }
|
877 |
|
878 |
|
879 | if (conditional.branch) {
|
880 | branches.push(booleanEnvTrace(conditional.condition) ? conditional.branch: '@empty');
|
881 | }
|
882 |
|
883 |
|
884 | else if (conditional.envs) {
|
885 | var doFallback = true;
|
886 | conditional.envs.forEach(function(env) {
|
887 | if (doFallback && booleanEnvTrace(env.condition))
|
888 | branches.push(env.branch);
|
889 |
|
890 |
|
891 |
|
892 | if (!booleanEnvTrace(conditionalComplement(env.condition)))
|
893 | doFallback = false;
|
894 | });
|
895 | if (doFallback && conditional.fallback)
|
896 | branches.push(conditional.fallback);
|
897 | }
|
898 |
|
899 |
|
900 | else if (conditional.branches) {
|
901 | Object.keys(conditional.branches).forEach(function(branch) {
|
902 | if (traceCondition(conditional.condition, branch))
|
903 | branches.push(conditional.branches[branch]);
|
904 | });
|
905 | }
|
906 |
|
907 | return {
|
908 | branches: branches,
|
909 | conditionModules: conditionModules,
|
910 | conditionalEnvVariations: conditionalEnvVariations
|
911 | };
|
912 | };
|
913 |
|
914 |
|
915 | Trace.getLoadDependencies = function(load, traceOpts, canonicalConditionalEnv, canonicalInlineConditionals) {
|
916 | canonicalConditionalEnv = canonicalConditionalEnv || {};
|
917 |
|
918 | var deps = [];
|
919 |
|
920 |
|
921 | if (load.conditional) {
|
922 | var resolutions = Trace.getConditionalResolutions(load.conditional, canonicalConditionalEnv, canonicalInlineConditionals);
|
923 |
|
924 | if (traceOpts.tracePackageConfig && load.packageConfig)
|
925 | deps.push(load.packageConfig);
|
926 |
|
927 | resolutions.conditionModules.forEach(function(conditionModule) {
|
928 | if (deps.indexOf(conditionModule) == -1)
|
929 | deps.push(conditionModule);
|
930 | });
|
931 |
|
932 | return deps.concat(resolutions.branches);
|
933 | }
|
934 |
|
935 |
|
936 | if (traceOpts.traceRuntimePlugin && load.runtimePlugin)
|
937 | deps.push(load.plugin);
|
938 |
|
939 |
|
940 | if (traceOpts.tracePackageConfig && load.pluginConfig)
|
941 | deps.push(load.pluginConfig);
|
942 |
|
943 |
|
944 | load.deps.forEach(function(dep) {
|
945 | deps.push(load.depMap[dep]);
|
946 | });
|
947 |
|
948 |
|
949 | if (traceOpts.tracePackageConfig && load.packageConfig)
|
950 | deps.push(load.packageConfig);
|
951 |
|
952 | return deps;
|
953 | };
|