systemjs/lib/extension-map.js
2014-06-08 12:20:23 -07:00

153 lines
4.0 KiB
JavaScript

/*
SystemJS map support
Provides map configuration through
System.map['jquery'] = 'some/module/map'
As well as contextual map config through
System.map['bootstrap'] = {
jquery: 'some/module/map2'
}
Note that this applies for subpaths, just like RequireJS
jquery -> 'some/module/map'
jquery/path -> 'some/module/map/path'
bootstrap -> 'bootstrap'
Inside any module name of the form 'bootstrap' or 'bootstrap/*'
jquery -> 'some/module/map2'
jquery/p -> 'some/module/map2/p'
Maps are carefully applied from most specific contextual map, to least specific global map
*/
function map(loader) {
loader.map = loader.map || {};
// return if prefix parts (separated by '/') match the name
// eg prefixMatch('jquery/some/thing', 'jquery') -> true
// prefixMatch('jqueryhere/', 'jquery') -> false
function prefixMatch(name, prefix) {
if (name.length < prefix.length)
return false;
if (name.substr(0, prefix.length) != prefix)
return false;
if (name[prefix.length] && name[prefix.length] != '/')
return false;
return true;
}
// get the depth of a given path
// eg pathLen('some/name') -> 2
function pathLen(name) {
var len = 1;
for (var i = 0, l = name.length; i < l; i++)
if (name[i] === '/')
len++;
return len;
}
function doMap(name, matchLen, map) {
return map + name.substr(matchLen);
}
// given a relative-resolved module name and normalized parent name,
// apply the map configuration
function applyMap(name, parentName, loader) {
var curMatch, curMatchLength = 0;
var curParent, curParentMatchLength = 0;
var tmpParentLength, tmpPrefixLength;
var subPath;
var nameParts;
// first find most specific contextual match
if (parentName) {
for (var p in loader.map) {
var curMap = loader.map[p];
if (typeof curMap != 'object')
continue;
// most specific parent match wins first
if (!prefixMatch(parentName, p))
continue;
tmpParentLength = pathLen(p);
if (tmpParentLength <= curParentMatchLength)
continue;
for (var q in curMap) {
// most specific name match wins
if (!prefixMatch(name, q))
continue;
tmpPrefixLength = pathLen(q);
if (tmpPrefixLength <= curMatchLength)
continue;
curMatch = q;
curMatchLength = tmpPrefixLength;
curParent = p;
curParentMatchLength = tmpParentLength;
}
}
}
// if we found a contextual match, apply it now
if (curMatch)
return doMap(name, curMatch.length, loader.map[curParent][curMatch]);
// now do the global map
for (var p in loader.map) {
var curMap = loader.map[p];
if (typeof curMap != 'string')
continue;
if (!prefixMatch(name, p))
continue;
var tmpPrefixLength = pathLen(p);
if (tmpPrefixLength <= curMatchLength)
continue;
curMatch = p;
curMatchLength = tmpPrefixLength;
}
if (curMatch)
return doMap(name, curMatch.length, loader.map[curMatch]);
return name;
}
var loaderNormalize = loader.normalize;
loader.normalize = function(name, parentName, parentAddress) {
var loader = this;
if (!loader.map)
loader.map = {};
var isPackage = false;
if (name.substr(name.length - 1, 1) == '/') {
isPackage = true;
name += '#';
}
return Promise.resolve(loaderNormalize.call(loader, name, parentName, parentAddress))
.then(function(name) {
name = applyMap(name, parentName, loader);
// Normalize "module/" into "module/module"
// Convenient for packages
if (isPackage) {
var nameParts = name.split('/');
nameParts.pop();
var pkgName = nameParts.pop();
nameParts.push(pkgName);
nameParts.push(pkgName);
name = nameParts.join('/');
}
return name;
});
}
}