diff options
Diffstat (limited to 'thirdparty/systemjs/lib/conditionals.js')
-rw-r--r-- | thirdparty/systemjs/lib/conditionals.js | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/thirdparty/systemjs/lib/conditionals.js b/thirdparty/systemjs/lib/conditionals.js new file mode 100644 index 000000000..d30a6f9ac --- /dev/null +++ b/thirdparty/systemjs/lib/conditionals.js @@ -0,0 +1,161 @@ +/* + * 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); + }); + }; + }); |