504 lines
15 KiB
JavaScript
504 lines
15 KiB
JavaScript
function readMemberExpression(p, value) {
|
|
var pParts = p.split('.');
|
|
while (pParts.length)
|
|
value = value[pParts.shift()];
|
|
return value;
|
|
}
|
|
|
|
function getMapMatch(map, name) {
|
|
var bestMatch, bestMatchLength = 0;
|
|
|
|
for (var p in map) {
|
|
if (name.substr(0, p.length) == p && (name.length == p.length || name[p.length] == '/')) {
|
|
var curMatchLength = p.split('/').length;
|
|
if (curMatchLength <= bestMatchLength)
|
|
continue;
|
|
bestMatch = p;
|
|
bestMatchLength = curMatchLength;
|
|
}
|
|
}
|
|
|
|
return bestMatch;
|
|
}
|
|
|
|
function prepareBaseURL(loader) {
|
|
// ensure baseURl is fully normalized
|
|
if (this._loader.baseURL !== this.baseURL) {
|
|
if (this.baseURL[this.baseURL.length - 1] != '/')
|
|
this.baseURL += '/';
|
|
|
|
this._loader.baseURL = this.baseURL = new URL(this.baseURL, baseURIObj).href;
|
|
}
|
|
}
|
|
|
|
var envModule;
|
|
function setProduction(isProduction, isBuilder) {
|
|
this.set('@system-env', envModule = this.newModule({
|
|
browser: isBrowser,
|
|
node: !!this._nodeRequire,
|
|
production: !isBuilder && isProduction,
|
|
dev: isBuilder || !isProduction,
|
|
build: isBuilder,
|
|
'default': true
|
|
}));
|
|
}
|
|
|
|
hookConstructor(function(constructor) {
|
|
return function() {
|
|
constructor.call(this);
|
|
|
|
// support baseURL
|
|
this.baseURL = baseURI;
|
|
|
|
// support map and paths
|
|
this.map = {};
|
|
|
|
// make the location of the system.js script accessible
|
|
if (typeof $__curScript != 'undefined')
|
|
this.scriptSrc = $__curScript.src;
|
|
|
|
// global behaviour flags
|
|
this.warnings = false;
|
|
this.defaultJSExtensions = false;
|
|
this.pluginFirst = false;
|
|
this.loaderErrorStack = false;
|
|
|
|
// by default load ".json" files as json
|
|
// leading * meta doesn't need normalization
|
|
// NB add this in next breaking release
|
|
// this.meta['*.json'] = { format: 'json' };
|
|
|
|
// support the empty module, as a concept
|
|
this.set('@empty', this.newModule({}));
|
|
|
|
setProduction.call(this, false, false);
|
|
};
|
|
});
|
|
|
|
// include the node require since we're overriding it
|
|
if (typeof require != 'undefined' && typeof process != 'undefined' && !process.browser)
|
|
SystemJSLoader.prototype._nodeRequire = require;
|
|
|
|
/*
|
|
Core SystemJS 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 paths
|
|
normalization phases.
|
|
|
|
The paths normalization phase applies last (paths extension), which
|
|
defines the `decanonicalize` function and normalizes everything into
|
|
a URL.
|
|
*/
|
|
|
|
var parentModuleContext;
|
|
function getNodeModule(name, baseURL) {
|
|
if (!isPlain(name))
|
|
throw new Error('Node module ' + name + ' can\'t be loaded as it is not a package require.');
|
|
|
|
if (!parentModuleContext) {
|
|
var Module = this._nodeRequire('module');
|
|
var base = baseURL.substr(isWindows ? 8 : 7);
|
|
parentModuleContext = new Module(base);
|
|
parentModuleContext.paths = Module._nodeModulePaths(base);
|
|
}
|
|
return parentModuleContext.require(name);
|
|
}
|
|
|
|
function coreResolve(name, parentName) {
|
|
// standard URL resolution
|
|
if (isRel(name))
|
|
return urlResolve(name, parentName);
|
|
else if (isAbsolute(name))
|
|
return name;
|
|
|
|
// plain names not starting with './', '://' and '/' go through custom resolution
|
|
var mapMatch = getMapMatch(this.map, name);
|
|
|
|
if (mapMatch) {
|
|
name = this.map[mapMatch] + name.substr(mapMatch.length);
|
|
|
|
if (isRel(name))
|
|
return urlResolve(name);
|
|
else if (isAbsolute(name))
|
|
return name;
|
|
}
|
|
|
|
if (this.has(name))
|
|
return name;
|
|
|
|
// dynamically load node-core modules when requiring `@node/fs` for example
|
|
if (name.substr(0, 6) == '@node/') {
|
|
if (!this._nodeRequire)
|
|
throw new TypeError('Error loading ' + name + '. Can only load node core modules in Node.');
|
|
if (this.builder)
|
|
this.set(name, this.newModule({}));
|
|
else
|
|
this.set(name, this.newModule(getESModule(getNodeModule.call(this, name.substr(6), this.baseURL))));
|
|
return name;
|
|
}
|
|
|
|
// prepare the baseURL to ensure it is normalized
|
|
prepareBaseURL.call(this);
|
|
|
|
return applyPaths(this, name) || this.baseURL + name;
|
|
}
|
|
|
|
hook('normalize', function(normalize) {
|
|
return function(name, parentName, skipExt) {
|
|
var resolved = coreResolve.call(this, name, parentName);
|
|
if (this.defaultJSExtensions && !skipExt && resolved.substr(resolved.length - 3, 3) != '.js' && !isPlain(resolved))
|
|
resolved += '.js';
|
|
return resolved;
|
|
};
|
|
});
|
|
|
|
// percent encode just '#' in urls if using HTTP requests
|
|
var httpRequest = typeof XMLHttpRequest != 'undefined';
|
|
hook('locate', function(locate) {
|
|
return function(load) {
|
|
return Promise.resolve(locate.call(this, load))
|
|
.then(function(address) {
|
|
if (httpRequest)
|
|
return address.replace(/#/g, '%23');
|
|
return address;
|
|
});
|
|
};
|
|
});
|
|
|
|
/*
|
|
* 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) {
|
|
if (parentName && parentName.name)
|
|
warn.call(this, 'SystemJS.import(name, { name: parentName }) is deprecated for SystemJS.import(name, parentName), while importing ' + name + ' from ' + parentName.name);
|
|
return systemImport.call(this, name, parentName, parentAddress).then(function(module) {
|
|
return module.__useDefault ? module['default'] : module;
|
|
});
|
|
};
|
|
});
|
|
|
|
/*
|
|
* Allow format: 'detect' meta to enable format detection
|
|
*/
|
|
hook('translate', function(systemTranslate) {
|
|
return function(load) {
|
|
if (load.metadata.format == 'detect')
|
|
load.metadata.format = undefined;
|
|
return systemTranslate.apply(this, arguments);
|
|
};
|
|
});
|
|
|
|
|
|
/*
|
|
* JSON format support
|
|
*
|
|
* Supports loading JSON files as a module format itself
|
|
*
|
|
* Usage:
|
|
*
|
|
* SystemJS.config({
|
|
* meta: {
|
|
* '*.json': { format: 'json' }
|
|
* }
|
|
* });
|
|
*
|
|
* Module is returned as if written:
|
|
*
|
|
* export default {JSON}
|
|
*
|
|
* No named exports are provided
|
|
*
|
|
* Files ending in ".json" are treated as json automatically by SystemJS
|
|
*/
|
|
hook('instantiate', function(instantiate) {
|
|
return function(load) {
|
|
if (load.metadata.format == 'json' && !this.builder) {
|
|
var entry = load.metadata.entry = createEntry();
|
|
entry.deps = [];
|
|
entry.execute = function() {
|
|
try {
|
|
return JSON.parse(load.source);
|
|
}
|
|
catch(e) {
|
|
throw new Error("Invalid JSON file " + load.name);
|
|
}
|
|
};
|
|
}
|
|
};
|
|
})
|
|
|
|
/*
|
|
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:
|
|
|
|
SystemJS.config({
|
|
meta: {
|
|
'./index.js': {}
|
|
}
|
|
});
|
|
|
|
To become
|
|
|
|
SystemJS.meta['https://thissite.com/index.js'] = {};
|
|
|
|
For easy normalization canonicalization with latest URL support.
|
|
|
|
*/
|
|
function envSet(loader, cfg, envCallback) {
|
|
if (envModule.browser && cfg.browserConfig)
|
|
envCallback(cfg.browserConfig);
|
|
if (envModule.node && cfg.nodeConfig)
|
|
envCallback(cfg.nodeConfig);
|
|
if (envModule.dev && cfg.devConfig)
|
|
envCallback(cfg.devConfig);
|
|
if (envModule.build && cfg.buildConfig)
|
|
envCallback(cfg.buildConfig);
|
|
if (envModule.production && cfg.productionConfig)
|
|
envCallback(cfg.productionConfig);
|
|
}
|
|
|
|
SystemJSLoader.prototype.getConfig = function(name) {
|
|
var cfg = {};
|
|
var loader = this;
|
|
for (var p in loader) {
|
|
if (loader.hasOwnProperty && !loader.hasOwnProperty(p) || p in SystemJSLoader.prototype && p != 'transpiler')
|
|
continue;
|
|
if (indexOf.call(['_loader', 'amdDefine', 'amdRequire', 'defined', 'failed', 'version', 'loads'], p) == -1)
|
|
cfg[p] = loader[p];
|
|
}
|
|
cfg.production = envModule.production;
|
|
return cfg;
|
|
};
|
|
|
|
var curCurScript;
|
|
SystemJSLoader.prototype.config = function(cfg, isEnvConfig) {
|
|
var loader = this;
|
|
|
|
if ('loaderErrorStack' in cfg) {
|
|
curCurScript = $__curScript;
|
|
if (cfg.loaderErrorStack)
|
|
$__curScript = undefined;
|
|
else
|
|
$__curScript = curCurScript;
|
|
}
|
|
|
|
if ('warnings' in cfg)
|
|
loader.warnings = cfg.warnings;
|
|
|
|
// transpiler deprecation path
|
|
if (cfg.transpilerRuntime === false)
|
|
loader._loader.loadedTranspilerRuntime = true;
|
|
|
|
if ('production' in cfg || 'build' in cfg)
|
|
setProduction.call(loader, !!cfg.production, !!(cfg.build || envModule && envModule.build));
|
|
|
|
if (!isEnvConfig) {
|
|
// if using nodeConfig / browserConfig / productionConfig, take baseURL from there
|
|
// these exceptions will be unnecessary when we can properly implement config queuings
|
|
var baseURL;
|
|
envSet(loader, cfg, function(cfg) {
|
|
baseURL = baseURL || cfg.baseURL;
|
|
});
|
|
baseURL = baseURL || cfg.baseURL;
|
|
|
|
// always configure baseURL first
|
|
if (baseURL) {
|
|
var hasConfig = false;
|
|
function checkHasConfig(obj) {
|
|
for (var p in obj)
|
|
if (obj.hasOwnProperty(p))
|
|
return true;
|
|
}
|
|
if (checkHasConfig(loader.packages) || checkHasConfig(loader.meta) || checkHasConfig(loader.depCache) || checkHasConfig(loader.bundles) || checkHasConfig(loader.packageConfigPaths))
|
|
throw new TypeError('Incorrect configuration order. The baseURL must be configured with the first SystemJS.config call.');
|
|
|
|
this.baseURL = baseURL;
|
|
prepareBaseURL.call(this);
|
|
}
|
|
|
|
if (cfg.paths)
|
|
extend(loader.paths, cfg.paths);
|
|
|
|
envSet(loader, cfg, function(cfg) {
|
|
if (cfg.paths)
|
|
extend(loader.paths, cfg.paths);
|
|
});
|
|
|
|
// warn on wildcard path deprecations
|
|
if (this.warnings) {
|
|
for (var p in loader.paths)
|
|
if (p.indexOf('*') != -1)
|
|
warn.call(loader, 'Paths configuration "' + p + '" -> "' + loader.paths[p] + '" uses wildcards which are being deprecated for simpler trailing "/" folder paths.');
|
|
}
|
|
}
|
|
|
|
if (cfg.defaultJSExtensions) {
|
|
loader.defaultJSExtensions = cfg.defaultJSExtensions;
|
|
warn.call(loader, 'The defaultJSExtensions configuration option is deprecated, use packages configuration instead.');
|
|
}
|
|
|
|
if (cfg.pluginFirst)
|
|
loader.pluginFirst = cfg.pluginFirst;
|
|
|
|
if (cfg.map) {
|
|
var objMaps = '';
|
|
for (var p in cfg.map) {
|
|
var v = cfg.map[p];
|
|
|
|
// object map backwards-compat into packages configuration
|
|
if (typeof v !== 'string') {
|
|
objMaps += (objMaps.length ? ', ' : '') + '"' + p + '"';
|
|
|
|
var defaultJSExtension = loader.defaultJSExtensions && p.substr(p.length - 3, 3) != '.js';
|
|
var prop = loader.decanonicalize(p);
|
|
if (defaultJSExtension && prop.substr(prop.length - 3, 3) == '.js')
|
|
prop = prop.substr(0, prop.length - 3);
|
|
|
|
// if a package main, revert it
|
|
var pkgMatch = '';
|
|
for (var pkg in loader.packages) {
|
|
if (prop.substr(0, pkg.length) == pkg
|
|
&& (!prop[pkg.length] || prop[pkg.length] == '/')
|
|
&& pkgMatch.split('/').length < pkg.split('/').length)
|
|
pkgMatch = pkg;
|
|
}
|
|
if (pkgMatch && loader.packages[pkgMatch].main)
|
|
prop = prop.substr(0, prop.length - loader.packages[pkgMatch].main.length - 1);
|
|
|
|
var pkg = loader.packages[prop] = loader.packages[prop] || {};
|
|
pkg.map = v;
|
|
}
|
|
else {
|
|
loader.map[p] = v;
|
|
}
|
|
}
|
|
if (objMaps)
|
|
warn.call(loader, 'The map configuration for ' + objMaps + ' uses object submaps, which is deprecated in global map.\nUpdate this to use package contextual map with configs like SystemJS.config({ packages: { "' + p + '": { map: {...} } } }).');
|
|
}
|
|
|
|
if (cfg.packageConfigPaths) {
|
|
var 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 = coreResolve.call(loader, path.substr(0, packageLength));
|
|
packageConfigPaths[i] = normalized + path.substr(packageLength);
|
|
}
|
|
loader.packageConfigPaths = packageConfigPaths;
|
|
}
|
|
|
|
if (cfg.bundles) {
|
|
for (var p in cfg.bundles) {
|
|
var bundle = [];
|
|
for (var i = 0; i < cfg.bundles[p].length; i++) {
|
|
var defaultJSExtension = loader.defaultJSExtensions && cfg.bundles[p][i].substr(cfg.bundles[p][i].length - 3, 3) != '.js';
|
|
var normalizedBundleDep = loader.decanonicalize(cfg.bundles[p][i]);
|
|
if (defaultJSExtension && normalizedBundleDep.substr(normalizedBundleDep.length - 3, 3) == '.js')
|
|
normalizedBundleDep = normalizedBundleDep.substr(0, normalizedBundleDep.length - 3);
|
|
bundle.push(normalizedBundleDep);
|
|
}
|
|
loader.bundles[p] = bundle;
|
|
}
|
|
}
|
|
|
|
if (cfg.packages) {
|
|
for (var p in cfg.packages) {
|
|
if (p.match(/^([^\/]+:)?\/\/$/))
|
|
throw new TypeError('"' + p + '" is not a valid package name.');
|
|
|
|
var prop = coreResolve.call(loader, p);
|
|
|
|
// allow trailing slash in packages
|
|
if (prop[prop.length - 1] == '/')
|
|
prop = prop.substr(0, prop.length - 1);
|
|
|
|
setPkgConfig(loader, prop, cfg.packages[p], false);
|
|
}
|
|
}
|
|
|
|
for (var c in cfg) {
|
|
var v = cfg[c];
|
|
|
|
if (indexOf.call(['baseURL', 'map', 'packages', 'bundles', 'paths', 'warnings', 'packageConfigPaths',
|
|
'loaderErrorStack', 'browserConfig', 'nodeConfig', 'devConfig', 'buildConfig', 'productionConfig'], c) != -1)
|
|
continue;
|
|
|
|
if (typeof v != 'object' || v instanceof Array) {
|
|
loader[c] = v;
|
|
}
|
|
else {
|
|
loader[c] = loader[c] || {};
|
|
|
|
for (var p in v) {
|
|
// base-level wildcard meta does not normalize to retain catch-all quality
|
|
if (c == 'meta' && p[0] == '*') {
|
|
extend(loader[c][p] = loader[c][p] || {}, v[p]);
|
|
}
|
|
else if (c == 'meta') {
|
|
// meta can go through global map, with defaultJSExtensions adding
|
|
var resolved = coreResolve.call(loader, p);
|
|
if (loader.defaultJSExtensions && resolved.substr(resolved.length - 3, 3) != '.js' && !isPlain(resolved))
|
|
resolved += '.js';
|
|
extend(loader[c][resolved] = loader[c][resolved] || {}, v[p]);
|
|
}
|
|
else if (c == 'depCache') {
|
|
var defaultJSExtension = loader.defaultJSExtensions && p.substr(p.length - 3, 3) != '.js';
|
|
var prop = loader.decanonicalize(p);
|
|
if (defaultJSExtension && prop.substr(prop.length - 3, 3) == '.js')
|
|
prop = prop.substr(0, prop.length - 3);
|
|
loader[c][prop] = [].concat(v[p]);
|
|
}
|
|
else {
|
|
loader[c][p] = v[p];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
envSet(loader, cfg, function(cfg) {
|
|
loader.config(cfg, true);
|
|
});
|
|
}; |