/* 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; var parsed = parsePlugin(loader, name); parentName = getParentName(this, parentName); 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, false) ]) .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.pluginLoader || 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; var args = arguments; if (load.metadata.loaderModule && load.metadata.loaderModule.translate && load.metadata.format != 'defined') { return Promise.resolve(load.metadata.loaderModule.translate.apply(loader, args)).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.address.split('!')[0]; // force set the filename of the original file if (!sourceMap.file || sourceMap.file == load.address) sourceMap.file = originalName + '!transpiled'; // force set the sources list if only one source if (!sourceMap.sources || sourceMap.sources.length <= 1 && (!sourceMap.sources[0] || sourceMap.sources[0] == load.address)) 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.apply(loader, args); }); } else { return translate.apply(loader, args); } }; }); 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); }; }); })();