261 lines
8.5 KiB
JavaScript
261 lines
8.5 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() {
|
||
|
function getParentName(loader, parentName) {
|
||
|
// if parent is a plugin, normalize against the parent plugin argument only
|
||
|
if (parentName) {
|
||
|
var parentPluginIndex;
|
||
|
if (loader.pluginFirst) {
|
||
|
if ((parentPluginIndex = parentName.lastIndexOf('!')) != -1)
|
||
|
return parentName.substr(parentPluginIndex + 1);
|
||
|
}
|
||
|
else {
|
||
|
if ((parentPluginIndex = parentName.indexOf('!')) != -1)
|
||
|
return parentName.substr(0, parentPluginIndex);
|
||
|
}
|
||
|
|
||
|
return parentName;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function parsePlugin(loader, name) {
|
||
|
var argumentName;
|
||
|
var pluginName;
|
||
|
|
||
|
var pluginIndex = name.lastIndexOf('!');
|
||
|
|
||
|
if (pluginIndex == -1)
|
||
|
return;
|
||
|
|
||
|
if (loader.pluginFirst) {
|
||
|
argumentName = name.substr(pluginIndex + 1);
|
||
|
pluginName = name.substr(0, pluginIndex);
|
||
|
}
|
||
|
else {
|
||
|
argumentName = name.substr(0, pluginIndex);
|
||
|
pluginName = name.substr(pluginIndex + 1) || argumentName.substr(argumentName.lastIndexOf('.') + 1);
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
argument: argumentName,
|
||
|
plugin: pluginName
|
||
|
};
|
||
|
}
|
||
|
|
||
|
// put name back together after parts have been normalized
|
||
|
function combinePluginParts(loader, argumentName, pluginName, defaultExtension) {
|
||
|
if (defaultExtension && argumentName.substr(argumentName.length - 3, 3) == '.js')
|
||
|
argumentName = argumentName.substr(0, argumentName.length - 3);
|
||
|
|
||
|
if (loader.pluginFirst) {
|
||
|
return pluginName + '!' + argumentName;
|
||
|
}
|
||
|
else {
|
||
|
return argumentName + '!' + pluginName;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// note if normalize will add a default js extension
|
||
|
// if so, remove for backwards compat
|
||
|
// this is strange and sucks, but will be deprecated
|
||
|
function checkDefaultExtension(loader, arg) {
|
||
|
return loader.defaultJSExtensions && arg.substr(arg.length - 3, 3) != '.js';
|
||
|
}
|
||
|
|
||
|
function createNormalizeSync(normalizeSync) {
|
||
|
return function(name, parentName, isPlugin) {
|
||
|
var loader = this;
|
||
|
|
||
|
parentName = getParentName(this, parentName);
|
||
|
var parsed = parsePlugin(loader, name);
|
||
|
|
||
|
if (!parsed)
|
||
|
return normalizeSync.call(this, name, parentName, isPlugin);
|
||
|
|
||
|
// if this is a plugin, normalize the plugin name and the argument
|
||
|
var argumentName = loader.normalizeSync(parsed.argument, parentName, true);
|
||
|
var pluginName = loader.normalizeSync(parsed.plugin, parentName, true);
|
||
|
return combinePluginParts(loader, argumentName, pluginName, checkDefaultExtension(loader, parsed.argument));
|
||
|
};
|
||
|
}
|
||
|
|
||
|
hook('decanonicalize', createNormalizeSync);
|
||
|
hook('normalizeSync', createNormalizeSync);
|
||
|
|
||
|
hook('normalize', function(normalize) {
|
||
|
return function(name, parentName, isPlugin) {
|
||
|
var loader = this;
|
||
|
|
||
|
parentName = getParentName(this, parentName);
|
||
|
|
||
|
var parsed = parsePlugin(loader, name);
|
||
|
|
||
|
if (!parsed)
|
||
|
return normalize.call(loader, name, parentName, isPlugin);
|
||
|
|
||
|
return Promise.all([
|
||
|
loader.normalize(parsed.argument, parentName, true),
|
||
|
loader.normalize(parsed.plugin, parentName)
|
||
|
])
|
||
|
.then(function(normalized) {
|
||
|
return combinePluginParts(loader, normalized[0], normalized[1], checkDefaultExtension(loader, parsed.argument));
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
|
||
|
hook('locate', function(locate) {
|
||
|
return function(load) {
|
||
|
var loader = this;
|
||
|
|
||
|
var name = load.name;
|
||
|
|
||
|
// plugin syntax
|
||
|
var pluginSyntaxIndex;
|
||
|
if (loader.pluginFirst) {
|
||
|
if ((pluginSyntaxIndex = name.indexOf('!')) != -1) {
|
||
|
load.metadata.loader = name.substr(0, pluginSyntaxIndex);
|
||
|
load.name = name.substr(pluginSyntaxIndex + 1);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if ((pluginSyntaxIndex = name.lastIndexOf('!')) != -1) {
|
||
|
load.metadata.loader = name.substr(pluginSyntaxIndex + 1);
|
||
|
load.name = name.substr(0, pluginSyntaxIndex);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return locate.call(loader, load)
|
||
|
.then(function(address) {
|
||
|
if (pluginSyntaxIndex != -1 || !load.metadata.loader)
|
||
|
return address;
|
||
|
|
||
|
// normalize plugin relative to parent in locate here when
|
||
|
// using plugin via loader metadata
|
||
|
return loader.normalize(load.metadata.loader, load.name)
|
||
|
.then(function(loaderNormalized) {
|
||
|
load.metadata.loader = loaderNormalized;
|
||
|
return address;
|
||
|
});
|
||
|
})
|
||
|
.then(function(address) {
|
||
|
var plugin = load.metadata.loader;
|
||
|
|
||
|
if (!plugin)
|
||
|
return address;
|
||
|
|
||
|
// don't allow a plugin to load itself
|
||
|
if (load.name == plugin)
|
||
|
throw new Error('Plugin ' + plugin + ' cannot load itself, make sure it is excluded from any wildcard meta configuration via a custom loader: false rule.');
|
||
|
|
||
|
// 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.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.format != 'defined') {
|
||
|
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 && load.metadata.format != 'defined') {
|
||
|
return Promise.resolve(load.metadata.loaderModule.translate.call(loader, load)).then(function(result) {
|
||
|
var sourceMap = load.metadata.sourceMap;
|
||
|
|
||
|
// sanitize sourceMap if an object not a JSON string
|
||
|
if (sourceMap) {
|
||
|
if (typeof sourceMap != 'object')
|
||
|
throw new Error('load.metadata.sourceMap must be set to an 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];
|
||
|
}
|
||
|
|
||
|
// if running on file:/// URLs, sourcesContent is necessary
|
||
|
// load.metadata.sourceMap.sourcesContent = [load.source];
|
||
|
|
||
|
if (typeof result == 'string')
|
||
|
load.source = result;
|
||
|
else
|
||
|
warn.call(this, 'Plugin ' + load.metadata.loader + ' should return the source in translate, instead of setting load.source directly. This support will be deprecated.');
|
||
|
|
||
|
return translate.call(loader, load);
|
||
|
});
|
||
|
}
|
||
|
else {
|
||
|
return translate.call(loader, load);
|
||
|
}
|
||
|
};
|
||
|
});
|
||
|
|
||
|
hook('instantiate', function(instantiate) {
|
||
|
return function(load) {
|
||
|
var loader = this;
|
||
|
var calledInstantiate = false;
|
||
|
|
||
|
if (load.metadata.loaderModule && load.metadata.loaderModule.instantiate && !loader.builder && load.metadata.format != 'defined')
|
||
|
return Promise.resolve(load.metadata.loaderModule.instantiate.call(loader, load, function(load) {
|
||
|
if (calledInstantiate)
|
||
|
throw new Error('Instantiate must only be called once.');
|
||
|
calledInstantiate = true;
|
||
|
return instantiate.call(loader, load);
|
||
|
})).then(function(result) {
|
||
|
if (calledInstantiate)
|
||
|
return result;
|
||
|
|
||
|
load.metadata.entry = createEntry();
|
||
|
load.metadata.entry.execute = function() {
|
||
|
return result;
|
||
|
}
|
||
|
load.metadata.entry.deps = load.metadata.deps;
|
||
|
load.metadata.format = 'defined';
|
||
|
return instantiate.call(loader, load);
|
||
|
});
|
||
|
else
|
||
|
return instantiate.call(loader, load);
|
||
|
};
|
||
|
});
|
||
|
|
||
|
})();
|