systemjs/lib/core.js
2015-09-05 13:22:37 +02:00

270 lines
7.5 KiB
JavaScript

var absURLRegEx = /^[^\/]+:\/\//;
function readMemberExpression(p, value) {
var pParts = p.split('.');
while (pParts.length)
value = value[pParts.shift()];
return value;
}
var baseURLCache = {};
function getBaseURLObj() {
if (baseURLCache[this.baseURL])
return baseURLCache[this.baseURL];
// normalize baseURL if not already
if (this.baseURL[this.baseURL.length - 1] != '/')
this.baseURL += '/';
var baseURL = new URL(this.baseURL, baseURI);
this.baseURL = baseURL.href;
return (baseURLCache[this.baseURL] = baseURL);
}
var baseURIObj = new URL(baseURI);
(function() {
hookConstructor(function(constructor) {
return function() {
constructor.call(this);
// support baseURL
this.baseURL = baseURI.substr(0, baseURI.lastIndexOf('/') + 1);
// support the empty module, as a concept
this.set('@empty', this.newModule({}));
};
});
/*
Normalization
If a name is relative, we apply URL normalization to the page
If a name is an absolute URL, we leave it as-is
Plain names (neither of the above) run through the map and package
normalization phases (applying before and after this one).
The paths normalization phase applies last (paths extension), which
defines the `normalizeSync` function and normalizes everything into
a URL.
The final normalization
*/
hook('normalize', function() {
return function(name, parentName) {
// relative URL-normalization
if (name[0] == '.' || name[0] == '/')
return new URL(name, parentName || baseURIObj).href;
return name;
};
});
/*
* Fetch with authorization
*/
hook('fetch', function() {
return function(load) {
return new Promise(function(resolve, reject) {
fetchTextFromURL(load.address, load.metadata.authorization, resolve, reject);
});
};
});
/*
__useDefault
When a module object looks like:
newModule(
__useDefault: true,
default: 'some-module'
})
Then importing that module provides the 'some-module'
result directly instead of the full module.
Useful for eg module.exports = function() {}
*/
hook('import', function(systemImport) {
return function(name, parentName, parentAddress) {
return systemImport.call(this, name, parentName, parentAddress).then(function(module) {
return module.__useDefault ? module['default'] : module;
});
};
});
/*
Extend config merging one deep only
loader.config({
some: 'random',
config: 'here',
deep: {
config: { too: 'too' }
}
});
<=>
loader.some = 'random';
loader.config = 'here'
loader.deep = loader.deep || {};
loader.deep.config = { too: 'too' };
Normalizes meta and package configs allowing for:
System.config({
meta: {
'./index.js': {}
}
});
To become
System.meta['https://thissite.com/index.js'] = {};
For easy normalization canonicalization with latest URL support.
*/
SystemJSLoader.prototype.deprecationWarnings = true;
SystemJSLoader.prototype.config = function(cfg) {
if ('deprecationWarnings' in cfg)
this.deprecationWarnings = cfg.deprecationWarnings;
// always configure baseURL first
if (cfg.baseURL) {
var hasConfig = false;
function checkHasConfig(obj) {
for (var p in obj)
return true;
}
if (checkHasConfig(this.packages) || checkHasConfig(this.meta) || checkHasConfig(this.depCache) || checkHasConfig(this.bundles))
throw new TypeError('baseURL should only be configured once and must be configured first.');
this.baseURL = cfg.baseURL;
// sanitize baseURL
getBaseURLObj.call(this);
}
if (cfg.defaultJSExtensions) {
this.defaultJSExtensions = cfg.defaultJSExtensions;
dWarn.call(this, 'The defaultJSExtensions configuration option is deprecated, use packages configuration instead.');
}
if (cfg.pluginFirst)
this.pluginFirst = cfg.pluginFirst;
if (cfg.paths) {
for (var p in cfg.paths)
this.paths[p] = cfg.paths[p];
}
if (cfg.map) {
for (var p in cfg.map) {
var v = cfg.map[p];
// object map backwards-compat into packages configuration
if (typeof v !== 'string') {
dWarn.call(this, 'The map configuration for "' + p + '" is an object, which is deprecated in global map.\nUpdate this to use package contextual map with System.config({ packages: { "' + p + '": { map: {...} } } }).');
var normalized = this.normalizeSync(p);
// if doing default js extensions, undo to get package name
if (this.defaultJSExtensions && p.substr(p.length - 3, 3) != '.js')
normalized = normalized.substr(0, normalized.length - 3);
// if a package main, revert it
var pkgMatch = '';
for (var pkg in this.packages) {
if (normalized.substr(0, pkg.length) == pkg
&& (!normalized[pkg.length] || normalized[pkg.length] == '/')
&& pkgMatch.split('/').length < pkg.split('/').length)
pkgMatch = pkg;
}
if (pkgMatch && this.packages[pkgMatch].main)
normalized = normalized.substr(0, normalized.length - this.packages[pkgMatch].main.length - 1);
var pkg = this.packages[normalized] = this.packages[normalized] || {};
pkg.map = v;
}
else {
this.map[p] = v;
}
}
}
if (cfg.packageConfigPaths) {
for (var i = 0; i < cfg.packageConfigPaths.length; i++) {
var path = cfg.packageConfigPaths[i];
var packageLength = Math.max(path.lastIndexOf('*') + 1, path.lastIndexOf('/'));
var normalized = this.normalizeSync(path.substr(0, packageLength) + '/');
if (this.defaultJSExtensions && path.substr(path.length - 3, 3) != '.js')
normalized = normalized.substr(0, normalized.length - 3);
cfg.packageConfigPaths[i] = normalized.substr(0, normalized.length - 1) + path.substr(packageLength);
}
}
if (cfg.packages) {
for (var p in cfg.packages) {
if (p.match(/^([^\/]+:)?\/\/$/))
throw new TypeError('"' + p + '" is not a valid package name.');
// request with trailing "/" to get package name exactly
var prop = this.normalizeSync(p + (p[p.length - 1] != '/' ? '/' : ''));
prop = prop.substr(0, prop.length - 1);
// if doing default js extensions, undo to get package name
if (this.defaultJSExtensions && p.substr(p.length - 3, 3) != '.js')
prop = prop.substr(0, prop.length - 3);
this.packages[prop]= this.packages[prop] || {};
for (var q in cfg.packages[p])
if (indexOf.call(packageProperties, q) == -1 && typeof console != 'undefined' && console.warn)
console.warn('"' + q + '" is not a valid package configuration option in package ' + p);
extendMeta(this.packages[prop], cfg.packages[p]);
}
}
if (cfg.bundles) {
for (var p in cfg.bundles) {
var bundle = [];
for (var i = 0; i < cfg.bundles[p].length; i++)
bundle.push(this.normalizeSync(cfg.bundles[p][i]));
this.bundles[p] = bundle;
}
}
for (var c in cfg) {
var v = cfg[c];
var normalizeProp = false, normalizeValArray = false;
if (c == 'baseURL' || c == 'map' || c == 'packages' || c == 'bundles' || c == 'paths' || c == 'deprecationWarnings')
continue;
if (typeof v != 'object' || v instanceof Array) {
this[c] = v;
}
else {
this[c] = this[c] || {};
if (c == 'meta' || c == 'depCache')
normalizeProp = true;
for (var p in v) {
if (c == 'meta' && p[0] == '*')
this[c][p] = v[p];
else if (normalizeProp)
this[c][this.normalizeSync(p)] = v[p];
else
this[c][p] = v[p];
}
}
}
};
})();