mirror of
https://github.com/systemjs/systemjs.git
synced 2026-01-18 14:53:14 +00:00
The "pluginFirst" is a boolean property of loader similar to defaultJSExensions. Setting it to true causes SystemJS to follow the plugin syntax of AMD and webpack, where the plugin name comes before the resource identifier. Old syntax was [resource ID]![plugin module ID]. With pluginFirst turned on, [plugin module ID]![resource ID]. See https://github.com/amdjs/amdjs-api/blob/master/LoaderPlugins.md for exact details. Should resolve issue #549. so require('./index.coffee!coffee') becomes require('coffee!./index.coffee") Note that the old anonymous plugin support like require("file.coffee!") which is short for require("file.coffee!coffee") is not enabled. require("!file.coffee") doesn't make as much sense but if necessary it can certainly be enabled. SystemJS builder may also need to be updated for complete support for this option.
212 lines
6.7 KiB
JavaScript
212 lines
6.7 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
|
|
if (parentName) {
|
|
var parentPluginIndex;
|
|
if (loader.pluginFirst) {
|
|
if ((parentPluginIndex = parentName.lastIndexOf('!')) != -1)
|
|
parentName = parentName.substr(parentPluginIndex + 1);
|
|
}
|
|
else {
|
|
if ((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;
|
|
var pluginName;
|
|
|
|
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);
|
|
}
|
|
|
|
// 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';
|
|
|
|
// put name back together after parts have been normalized
|
|
function normalizePluginParts(argumentName, pluginName) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (sync) {
|
|
argumentName = loader.normalizeSync(argumentName, parentName);
|
|
pluginName = loader.normalizeSync(pluginName, parentName);
|
|
|
|
return normalizePluginParts(argumentName, pluginName);
|
|
}
|
|
else {
|
|
return Promise.all([
|
|
loader.normalize(argumentName, parentName),
|
|
loader.normalize(pluginName, parentName)
|
|
])
|
|
.then(function(normalized) {
|
|
return normalizePluginParts(normalized[0], 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;
|
|
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) {
|
|
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);
|
|
};
|
|
});
|
|
|
|
})();
|