systemjs/lib/plugins.js

180 lines
5.9 KiB
JavaScript

/*
SystemJS Loader Plugin Support
Supports plugin loader syntax with "!", or via metadata.loader
The plugin name is loaded as a module itself, and can override standard loader hooks
for the plugin resource. See the plugin section of the systemjs readme.
*/
(function() {
// sync or async plugin normalize function
function normalizePlugin(normalize, name, parentName, sync) {
var loader = this;
// if parent is a plugin, normalize against the parent plugin argument only
var parentPluginIndex;
if (parentName && (parentPluginIndex = parentName.indexOf('!')) != -1)
parentName = parentName.substr(0, parentPluginIndex);
// if this is a plugin, normalize the plugin name and the argument
var pluginIndex = name.lastIndexOf('!');
if (pluginIndex != -1) {
var argumentName = name.substr(0, pluginIndex);
var pluginName = name.substr(pluginIndex + 1) || argumentName.substr(argumentName.lastIndexOf('.') + 1);
// note if normalize will add a default js extension
// if so, remove for backwards compat
// this is strange and sucks, but will be deprecated
var defaultExtension = loader.defaultJSExtensions && argumentName.substr(argumentName.length - 3, 3) != '.js';
if (sync) {
argumentName = loader.normalizeSync(argumentName, parentName);
pluginName = loader.normalizeSync(pluginName, parentName);
if (defaultExtension && argumentName.substr(argumentName.length - 3, 3) == '.js')
argumentName = argumentName.substr(0, argumentName.length - 3);
return argumentName + '!' + pluginName;
}
else {
return Promise.all([
loader.normalize(argumentName, parentName),
loader.normalize(pluginName, parentName)
])
.then(function(normalized) {
argumentName = normalized[0];
if (defaultExtension && argumentName.substr(argumentName.length - 3, 3) == '.js')
argumentName = argumentName.substr(0, argumentName.length - 3);
return argumentName + '!' + normalized[1];
});
}
}
else {
return normalize.call(loader, name, parentName);
}
}
// async plugin normalize
hook('normalize', function(normalize) {
return function(name, parentName) {
return normalizePlugin.call(this, normalize, name, parentName, false);
};
});
hook('normalizeSync', function(normalizeSync) {
return function(name, parentName) {
return normalizePlugin.call(this, normalizeSync, name, parentName, true);
};
});
hook('locate', function(locate) {
return function(load) {
var loader = this;
var name = load.name;
// plugin syntax
var pluginSyntaxIndex = name.lastIndexOf('!');
if (pluginSyntaxIndex != -1) {
load.metadata.loader = name.substr(pluginSyntaxIndex + 1);
load.name = name.substr(0, pluginSyntaxIndex);
}
return locate.call(loader, load)
.then(function(address) {
var plugin = load.metadata.loader;
if (!plugin)
return address;
// only fetch the plugin itself if this name isn't defined
if (loader.defined && loader.defined[name])
return address;
var pluginLoader = loader.pluginLoader || loader;
// load the plugin module and run standard locate
return pluginLoader['import'](plugin)
.then(function(loaderModule) {
// store the plugin module itself on the metadata
load.metadata.loaderModule = loaderModule;
load.metadata.loaderArgument = name;
load.address = address;
if (loaderModule.locate)
return loaderModule.locate.call(loader, load);
return address;
});
});
};
});
hook('fetch', function(fetch) {
return function(load) {
var loader = this;
if (load.metadata.loaderModule && load.metadata.loaderModule.fetch) {
load.metadata.scriptLoad = false;
return load.metadata.loaderModule.fetch.call(loader, load, function(load) {
return fetch.call(loader, load);
});
}
else {
return fetch.call(loader, load);
}
};
});
hook('translate', function(translate) {
return function(load) {
var loader = this;
if (load.metadata.loaderModule && load.metadata.loaderModule.translate)
return Promise.resolve(load.metadata.loaderModule.translate.call(loader, load)).then(function(result) {
if (typeof result == 'string')
load.source = result;
return translate.call(loader, load);
});
else
return translate.call(loader, load);
};
});
hook('instantiate', function(instantiate) {
return function(load) {
var loader = this;
/*
* Source map sanitization for load.metadata.sourceMap
* Used to set browser and build-level source maps for
* translated sources in a general way.
*/
var sourceMap = load.metadata.sourceMap;
// if an object not a JSON string do sanitizing
if (sourceMap && typeof sourceMap == 'object') {
var originalName = load.name.split('!')[0];
// force set the filename of the original file
sourceMap.file = originalName + '!transpiled';
// force set the sources list if only one source
if (!sourceMap.sources || sourceMap.sources.length == 1)
sourceMap.sources = [originalName];
load.metadata.sourceMap = JSON.stringify(sourceMap);
}
if (load.metadata.loaderModule && load.metadata.loaderModule.instantiate)
return Promise.resolve(load.metadata.loaderModule.instantiate.call(loader, load)).then(function(result) {
load.metadata.format = 'defined';
load.metadata.execute = function() {
return result;
};
return instantiate.call(loader, load);
});
else
return instantiate.call(loader, load);
};
});
})();