wallet-core/thirdparty/systemjs/lib/conditionals.js

162 lines
5.6 KiB
JavaScript

/*
* Conditions Extension
*
* Allows a condition module to alter the resolution of an import via syntax:
*
* import $ from 'jquery/#{browser}';
*
* Will first load the module 'browser' via `SystemJS.import('browser')` and
* take the default export of that module.
* If the default export is not a string, an error is thrown.
*
* We then substitute the string into the require to get the conditional resolution
* enabling environment-specific variations like:
*
* import $ from 'jquery/ie'
* import $ from 'jquery/firefox'
* import $ from 'jquery/chrome'
* import $ from 'jquery/safari'
*
* It can be useful for a condition module to define multiple conditions.
* This can be done via the `|` modifier to specify an export member expression:
*
* import 'jquery/#{./browser.js|grade.version}'
*
* Where the `grade` export `version` member in the `browser.js` module is substituted.
*
*
* Boolean Conditionals
*
* For polyfill modules, that are used as imports but have no module value,
* a binary conditional allows a module not to be loaded at all if not needed:
*
* import 'es5-shim#?./conditions.js|needs-es5shim'
*
* These conditions can also be negated via:
*
* import 'es5-shim#?./conditions.js|~es6'
*
*/
var sysConditions = ['browser', 'node', 'dev', 'build', 'production', 'default'];
function parseCondition(condition) {
var conditionExport, conditionModule, negation;
var negation = condition[0] == '~';
var conditionExportIndex = condition.lastIndexOf('|');
if (conditionExportIndex != -1) {
conditionExport = condition.substr(conditionExportIndex + 1);
conditionModule = condition.substr(negation, conditionExportIndex - negation);
if (negation)
warn.call(this, 'Condition negation form "' + condition + '" is deprecated for "' + conditionModule + '|~' + conditionExport + '"');
if (conditionExport[0] == '~') {
negation = true;
conditionExport = conditionExport.substr(1);
}
}
else {
conditionExport = 'default';
conditionModule = condition.substr(negation);
if (sysConditions.indexOf(conditionModule) != -1) {
conditionExport = conditionModule;
conditionModule = null;
}
}
return {
module: conditionModule || '@system-env',
prop: conditionExport,
negate: negation
};
}
function serializeCondition(conditionObj) {
return conditionObj.module + '|' + (conditionObj.negate ? '~' : '') + conditionObj.prop;
}
function resolveCondition(conditionObj, parentName, bool) {
var self = this;
return this.normalize(conditionObj.module, parentName)
.then(function(normalizedCondition) {
return self.load(normalizedCondition)
.then(function(q) {
var m = readMemberExpression(conditionObj.prop, self.get(normalizedCondition));
if (bool && typeof m != 'boolean')
throw new TypeError('Condition ' + serializeCondition(conditionObj) + ' did not resolve to a boolean.');
return conditionObj.negate ? !m : m;
});
});
}
var interpolationRegEx = /#\{[^\}]+\}/;
function interpolateConditional(name, parentName) {
// first we normalize the conditional
var conditionalMatch = name.match(interpolationRegEx);
if (!conditionalMatch)
return Promise.resolve(name);
var conditionObj = parseCondition.call(this, conditionalMatch[0].substr(2, conditionalMatch[0].length - 3));
// in builds, return normalized conditional
if (this.builder)
return this['normalize'](conditionObj.module, parentName)
.then(function(conditionModule) {
conditionObj.module = conditionModule;
return name.replace(interpolationRegEx, '#{' + serializeCondition(conditionObj) + '}');
});
return resolveCondition.call(this, conditionObj, parentName, false)
.then(function(conditionValue) {
if (typeof conditionValue !== 'string')
throw new TypeError('The condition value for ' + name + ' doesn\'t resolve to a string.');
if (conditionValue.indexOf('/') != -1)
throw new TypeError('Unabled to interpolate conditional ' + name + (parentName ? ' in ' + parentName : '') + '\n\tThe condition value ' + conditionValue + ' cannot contain a "/" separator.');
return name.replace(interpolationRegEx, conditionValue);
});
}
function booleanConditional(name, parentName) {
// first we normalize the conditional
var booleanIndex = name.lastIndexOf('#?');
if (booleanIndex == -1)
return Promise.resolve(name);
var conditionObj = parseCondition.call(this, name.substr(booleanIndex + 2));
// in builds, return normalized conditional
if (this.builder)
return this['normalize'](conditionObj.module, parentName)
.then(function(conditionModule) {
conditionObj.module = conditionModule;
return name.substr(0, booleanIndex) + '#?' + serializeCondition(conditionObj);
});
return resolveCondition.call(this, conditionObj, parentName, true)
.then(function(conditionValue) {
return conditionValue ? name.substr(0, booleanIndex) : '@empty';
});
}
// normalizeSync does not parse conditionals at all although it could
hook('normalize', function(normalize) {
return function(name, parentName, skipExt) {
var loader = this;
return booleanConditional.call(loader, name, parentName)
.then(function(name) {
return normalize.call(loader, name, parentName, skipExt);
})
.then(function(normalized) {
return interpolateConditional.call(loader, normalized, parentName);
});
};
});