Skip to content
This repository has been archived by the owner on Oct 9, 2020. It is now read-only.

Commit

Permalink
refactor for early conditional canonicalization
Browse files Browse the repository at this point in the history
  • Loading branch information
guybedford committed Sep 15, 2016
1 parent e888dfa commit 941315e
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 136 deletions.
33 changes: 30 additions & 3 deletions lib/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,10 @@ Builder.prototype.trace = function(expression, opts) {

var traceOpts = processTraceOpts(opts);
var self = this;
return setExternals(this, traceOpts.externals)
return canonicalizeConditions(self.loader, traceOpts)
.then(function() {
return setExternals(this, traceOpts.externals);
})
.then(function() {
return traceExpression(self, expression, traceOpts);
});
Expand Down Expand Up @@ -537,16 +540,20 @@ Builder.prototype.compile = function(moduleNameOrLoad, outFile, opts) {

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),
processTraceOpts(opts, { tracePackageConfig: false, browser: true, node: false, production: true, dev: false, outputESM: false }));
return self.tracer.getLoadRecord(getCanonicalName(self.loader, moduleName), traceOpts);
});
})
.then(function(load) {
Expand Down Expand Up @@ -633,6 +640,20 @@ function setExternals(builder, externals) {
});
}

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') {
Expand All @@ -656,6 +677,9 @@ Builder.prototype.bundle = function(expressionOrTree, outFile, opts) {
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;
Expand Down Expand Up @@ -730,6 +754,9 @@ Builder.prototype.buildStatic = function(expressionOrTree, outFile, opts) {
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;
Expand Down
247 changes: 114 additions & 133 deletions lib/trace.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,47 +48,35 @@ Trace.prototype.traceModule = function(moduleName, traceOpts) {

// given a module, return the conditional environment variation space
Trace.getConditionalEnv = function(builder, loads, traceOpts) {
return toCanonicalConditionalEnv.call(builder, traceOpts.conditions)
.then(function(canonicalConditionalEnv) {
return toCanonicalConditionalEnv.call(builder, traceOpts.inlineConditions)
.then(function(canonicalInlineConditionals) {
var conditionalEnvVariations = {};

Object.keys(loads).forEach(function(canonical) {
var load = loads[canonical];
if (!load.conditional)
return;

var envVariations = Trace.getConditionalResolutions(load.conditional, canonicalConditionalEnv, canonicalInlineConditionals).conditionalEnvVariations;

Object.keys(envVariations).forEach(function(condition) {
if (envVariations[condition] instanceof Array) {
conditionalEnvVariations[condition] = conditionalEnvVariations[condition] || [];
envVariations[condition].forEach(function(conditionValue) {
if (conditionalEnvVariations[condition].indexOf(conditionValue) === -1)
conditionalEnvVariations[condition].push(conditionValue);
});
}
else {
conditionalEnvVariations[condition] = conditionalEnvVariations[condition] || envVariations[condition];
}
var conditionalEnvVariations = {};

Object.keys(loads).forEach(function(canonical) {
var load = loads[canonical];
if (!load.conditional)
return;

var envVariations = Trace.getConditionalResolutions(load.conditional, traceOpts.conditions, traceOpts.inlineConditions).conditionalEnvVariations;

Object.keys(envVariations).forEach(function(condition) {
if (envVariations[condition] instanceof Array) {
conditionalEnvVariations[condition] = conditionalEnvVariations[condition] || [];
envVariations[condition].forEach(function(conditionValue) {
if (conditionalEnvVariations[condition].indexOf(conditionValue) === -1)
conditionalEnvVariations[condition].push(conditionValue);
});
});
return conditionalEnvVariations;
}
else {
conditionalEnvVariations[condition] = conditionalEnvVariations[condition] || envVariations[condition];
}
});
});
return conditionalEnvVariations;
};

Trace.prototype.traceCanonical = function(canonical, traceOpts) {
var self = this;

return toCanonicalConditionalEnv.call(self, traceOpts.conditions)
.then(function(canonicalConditionalEnv) {
return toCanonicalConditionalEnv.call(self, traceOpts.inlineConditions)
.then(function(canonicalInlineConditionals) {
return self.getAllLoadRecords(canonical, traceOpts, canonicalConditionalEnv, canonicalInlineConditionals, {}, []);
});
})
return self.getAllLoadRecords(canonical, traceOpts, traceOpts.conditions, traceOpts.inlineConditions, {}, [])
.then(function(loads) {
// if it is a bundle, we just use a regex to extract the list of loads
// as "true" records for subtraction arithmetic use only
Expand Down Expand Up @@ -654,9 +642,8 @@ function conditionalComplement(condition) {
return serializeCondition(conditionObj);
}

function toCanonicalConditionalEnv(conditionalEnv) {
var loader = this.loader;

Trace.toCanonicalConditionalEnv = toCanonicalConditionalEnv;
function toCanonicalConditionalEnv(loader, conditionalEnv) {
var canonicalConditionalEnv = {};

// convert boolean conditions of the form
Expand Down Expand Up @@ -715,121 +702,115 @@ function toCanonicalConditionalEnv(conditionalEnv) {
Trace.prototype.inlineConditions = function(tree, loader, traceOpts) {
var self = this;

return toCanonicalConditionalEnv.call(self, traceOpts.conditions)
.then(function(canonicalConditionalEnv) {
return toCanonicalConditionalEnv.call(self, traceOpts.inlineConditions)
.then(function(canonicalInlineConditionals) {
var inconsistencyErrorMsg = 'For static condition inlining only an exact environment resolution can be built, pending https://github.com/systemjs/builder/issues/311.';
var inconsistencyErrorMsg = 'For static condition inlining only an exact environment resolution can be built, pending https://github.com/systemjs/builder/issues/311.';

// ensure we have no condition conflicts
for (var c in canonicalInlineConditionals) {
if (canonicalInlineConditionals[c].length > 1)
throw new TypeError('Error building condition ' + c + '. ' + inconsistencyErrorMsg);
}
// ensure we have no condition conflicts
for (var c in traceOpts.inlineConditions) {
if (traceOpts.inlineConditions[c].length > 1)
throw new TypeError('Error building condition ' + c + '. ' + inconsistencyErrorMsg);
}

var conditionalResolutions = {};
var importsSystemEnv = false;
var conditionalResolutions = {};
var importsSystemEnv = false;

// get the list of all conditional dependencies in the tree
var allConditionals = [];
Object.keys(tree).forEach(function(m) {
if (m.match(/#[\:\?\{]/) && allConditionals.indexOf(m) === -1)
allConditionals.push(m);
// get the list of all conditional dependencies in the tree
var allConditionals = [];
Object.keys(tree).forEach(function(m) {
if (m.match(/#[\:\?\{]/) && allConditionals.indexOf(m) === -1)
allConditionals.push(m);

if (!tree[m] || tree[m].conditional)
return;
if (!tree[m] || tree[m].conditional)
return;

Object.keys(tree[m].depMap).forEach(function(d) {
var dep = tree[m].depMap[d];
Object.keys(tree[m].depMap).forEach(function(d) {
var dep = tree[m].depMap[d];

if (dep.match(/#[\:\?\{]/) && allConditionals.indexOf(dep) === -1)
allConditionals.push(dep);
});
});
if (dep.match(/#[\:\?\{]/) && allConditionals.indexOf(dep) === -1)
allConditionals.push(dep);
});
});

// determine the conditional resolutions
return Promise.all(allConditionals.map(function(c) {
return self.getLoadRecord(c, traceOpts)
.then(function(load) {
var branches = Trace.getConditionalResolutions(load.conditional, canonicalConditionalEnv, canonicalInlineConditionals).branches;
// determine the conditional resolutions
return Promise.all(allConditionals.map(function(c) {
return self.getLoadRecord(c, traceOpts)
.then(function(load) {
var branches = Trace.getConditionalResolutions(load.conditional, traceOpts.conditions, traceOpts.inlineConditions).branches;

if (branches.length === 1)
conditionalResolutions[c] = branches[0];
});
}))
.then(function() {
// resolve any chained conditionals
Object.keys(conditionalResolutions).forEach(function(c) {
var resolution = conditionalResolutions[c];

var seen = [];
while (conditionalResolutions[resolution] && seen.indexOf(resolution) == -1) {
seen.push(resolution);
resolution = conditionalResolutions[resolution];
conditionalResolutions[c] = resolution;
}
if (seen.indexOf(resolution) != -1)
throw new Error('Circular conditional resolution ' + seen.join(' -> ') + ' -> ' + resolution);
});
if (branches.length === 1)
conditionalResolutions[c] = branches[0];
});
}))
.then(function() {
// resolve any chained conditionals
Object.keys(conditionalResolutions).forEach(function(c) {
var resolution = conditionalResolutions[c];

var seen = [];
while (conditionalResolutions[resolution] && seen.indexOf(resolution) == -1) {
seen.push(resolution);
resolution = conditionalResolutions[resolution];
conditionalResolutions[c] = resolution;
}
if (seen.indexOf(resolution) != -1)
throw new Error('Circular conditional resolution ' + seen.join(' -> ') + ' -> ' + resolution);
});

// finally we do a deep clone of the tree, applying the conditional resolutions as we go
// if we have a dependency on a condition not in the tree, we throw as it would be an unresolved external
var inlinedTree = {};
Object.keys(tree).forEach(function(m) {
var load = tree[m];
// finally we do a deep clone of the tree, applying the conditional resolutions as we go
// if we have a dependency on a condition not in the tree, we throw as it would be an unresolved external
var inlinedTree = {};
Object.keys(tree).forEach(function(m) {
var load = tree[m];

if (typeof load == 'boolean') {
inlinedTree[m] = load;
return;
}
if (typeof load == 'boolean') {
inlinedTree[m] = load;
return;
}

if (load.conditional)
return;
if (load.conditional)
return;

var clonedLoad = extend({}, load);
clonedLoad.depMap = {};
Object.keys(load.depMap).forEach(function(d) {
var normalizedDep = load.depMap[d];
var clonedLoad = extend({}, load);
clonedLoad.depMap = {};
Object.keys(load.depMap).forEach(function(d) {
var normalizedDep = load.depMap[d];

normalizedDep = conditionalResolutions[normalizedDep] || normalizedDep;
normalizedDep = conditionalResolutions[normalizedDep] || normalizedDep;

if (normalizedDep == '@system-env')
importsSystemEnv = true;
if (normalizedDep == '@system-env')
importsSystemEnv = true;

clonedLoad.depMap[d] = normalizedDep;
});
clonedLoad.depMap[d] = normalizedDep;
});

inlinedTree[m] = clonedLoad;
});
inlinedTree[m] = clonedLoad;
});

// if we explicitly import from the system environment, then we need to build it into a static build
// this is normally excluded as it is a system module in SystemJS but won't be available in static
// builds which is exactly what this function acts on
if (importsSystemEnv) {
inlinedTree['@system-env'] = {
name: '@system-env',
path: null,
metadata: {
format: 'json'
},
deps: [],
depMap: {},
source: JSON.stringify({
production: canonicalInlineConditionals['@system-env|production'],
browser: canonicalInlineConditionals['@system-env|browser'],
node: canonicalInlineConditionals['@system-env|node'],
dev: canonicalInlineConditionals['@system-env|dev'],
default: true
}),
fresh: true,
timestamp: null,
configHash: loader.configHash,
};
}
// if we explicitly import from the system environment, then we need to build it into a static build
// this is normally excluded as it is a system module in SystemJS but won't be available in static
// builds which is exactly what this function acts on
if (importsSystemEnv) {
inlinedTree['@system-env'] = {
name: '@system-env',
path: null,
metadata: {
format: 'json'
},
deps: [],
depMap: {},
source: JSON.stringify({
production: traceOpts.inlineConditions['@system-env|production'],
browser: traceOpts.inlineConditions['@system-env|browser'],
node: traceOpts.inlineConditions['@system-env|node'],
dev: traceOpts.inlineConditions['@system-env|dev'],
default: true
}),
fresh: true,
timestamp: null,
configHash: loader.configHash,
};
}

return inlinedTree;
});
});
return inlinedTree;
});
};

Expand Down

0 comments on commit 941315e

Please sign in to comment.