steal
Version:
Gets JavaScript.
1,842 lines (1,594 loc) • 142 kB
JavaScript
(function(global){
// helpers
var camelize = function(str){
return str.replace(/-+(.)?/g, function(match, chr){
return chr ? chr.toUpperCase() : ''
});
},
each = function( o, cb){
var i, len;
// weak array detection, but we only use this internally so don't
// pass it weird stuff
if ( typeof o.length == 'number' && (o.length - 1) in o) {
for ( i = 0, len = o.length; i < len; i++ ) {
cb.call(o[i], o[i], i, o);
}
} else {
for ( i in o ) {
if(o.hasOwnProperty(i)){
cb.call(o[i], o[i], i, o);
}
}
}
return o;
},
map = function(o, cb) {
var arr = [];
each(o, function(item, i){
arr[i] = cb(item, i);
});
return arr;
},
isString = function(o) {
return typeof o == "string";
},
extend = function(d,s){
each(s, function(v, p){
d[p] = v;
});
return d;
},
dir = function(uri){
var lastSlash = uri.lastIndexOf("/");
//if no / slashes, check for \ slashes since it might be a windows path
if(lastSlash === -1)
lastSlash = uri.lastIndexOf("\\");
if(lastSlash !== -1) {
return uri.substr(0, lastSlash);
} else {
return uri;
}
},
last = function(arr){
return arr[arr.length - 1];
},
parseURI = function(url) {
var m = String(url).replace(/^\s+|\s+$/g, '').match(/^([^:\/?#]+:)?(\/\/(?:[^:@\/]*(?::[^:@\/]*)?@)?(([^:\/?#]*)(?::(\d*))?))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?/);
// authority = '//' + user + ':' + pass '@' + hostname + ':' port
return (m ? {
href : m[0] || '',
protocol : m[1] || '',
authority: m[2] || '',
host : m[3] || '',
hostname : m[4] || '',
port : m[5] || '',
pathname : m[6] || '',
search : m[7] || '',
hash : m[8] || ''
} : null);
},
joinURIs = function(base, href) {
function removeDotSegments(input) {
var output = [];
input.replace(/^(\.\.?(\/|$))+/, '')
.replace(/\/(\.(\/|$))+/g, '/')
.replace(/\/\.\.$/, '/../')
.replace(/\/?[^\/]*/g, function (p) {
if (p === '/..') {
output.pop();
} else {
output.push(p);
}
});
return output.join('').replace(/^\//, input.charAt(0) === '/' ? '/' : '');
}
href = parseURI(href || '');
base = parseURI(base || '');
return !href || !base ? null : (href.protocol || base.protocol) +
(href.protocol || href.authority ? href.authority : base.authority) +
removeDotSegments(href.protocol || href.authority || href.pathname.charAt(0) === '/' ? href.pathname : (href.pathname ? ((base.authority && !base.pathname ? '/' : '') + base.pathname.slice(0, base.pathname.lastIndexOf('/') + 1) + href.pathname) : base.pathname)) +
(href.protocol || href.authority || href.pathname ? href.search : (href.search || base.search)) +
href.hash;
},
relativeURI = function(base, path) {
var uriParts = path.split("/"),
baseParts = base.split("/"),
result = [];
while ( uriParts.length && baseParts.length && uriParts[0] == baseParts[0] ) {
uriParts.shift();
baseParts.shift();
}
for(var i = 0 ; i< baseParts.length-1; i++) {
result.push("../");
}
return "./" + result.join("") + uriParts.join("/");
},
fBind = Function.prototype.bind,
isFunction = function(obj) {
return !!(obj && obj.constructor && obj.call && obj.apply);
},
isWebWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope,
isNode = typeof process === "object" && {}.toString.call(process) === "[object process]",
isBrowserWithWindow = !isNode && typeof window !== "undefined",
isNW = isNode && (function(){
try {
return require("nw.gui") !== "undefined";
} catch(e) {
return false;
}
})(),
isElectron = isNode && !!process.versions["electron"],
isNode = isNode && !isNW && !isElectron,
hasAWindow = isBrowserWithWindow || isNW || isElectron,
getStealScript = function(){
if(isBrowserWithWindow || isNW || isElectron) {
if(document.currentScript) {
return document.currentScript;
}
var scripts = document.scripts;
if (scripts.length) {
var currentScript = scripts[scripts.length - 1];
return currentScript;
}
}
},
stealScript = getStealScript(),
warn = typeof console === "object" ?
fBind.call(console.warn, console) : function(){};
var filename = function(uri){
var lastSlash = uri.lastIndexOf("/");
//if no / slashes, check for \ slashes since it might be a windows path
if(lastSlash === -1)
lastSlash = uri.lastIndexOf("\\");
var matches = ( lastSlash == -1 ? uri : uri.substr(lastSlash+1) ).match(/^[\w-\s\.!]+/);
return matches ? matches[0] : "";
};
var ext = function(uri){
var fn = filename(uri);
var dot = fn.lastIndexOf(".");
if(dot !== -1) {
return fn.substr(dot+1);
} else {
return "";
}
};
var pluginCache = {};
var normalize = function(unnormalizedName, loader){
var name = unnormalizedName;
// Detech if this name contains a plugin part like: app.less!steal/less
// and catch the plugin name so that when it is normalized we do not perform
// Steal's normalization against it.
var pluginIndex = name.lastIndexOf('!');
var pluginPart = "";
if (pluginIndex != -1) {
// argumentName is the part before the !
var argumentName = name.substr(0, pluginIndex);
var pluginName = name.substr(pluginIndex + 1);
pluginPart = "!" + pluginName;
// Set the name to the argument name so that we can normalize it alone.
name = argumentName;
}
var last = filename(name),
extension = ext(name);
// if the name ends with /
if( name[name.length -1] === "/" ) {
return name+filename( name.substr(0, name.length-1) ) + pluginPart;
} else if( !/^(\w+(?:s)?:\/\/|\.|file|\/)/.test(name) &&
// and doesn't end with a dot
last.indexOf(".") === -1
) {
return name+"/"+last + pluginPart;
} else {
if(extension === "js") {
return name.substr(0, name.lastIndexOf(".")) + pluginPart;
} else {
return name + pluginPart;
}
}
};
var cloneSteal = function(System){
var loader = System || this.System;
var steal = makeSteal(loader.clone());
steal.loader.set("@steal", steal.loader.newModule({
"default": steal,
__useDefault: true
}));
steal.clone = cloneSteal;
return steal;
};
var ArraySet;
if(typeof Set === "function") {
ArraySet = Set;
} else {
ArraySet = function(){ this._items = []; };
ArraySet.prototype.has = function(item) {
return this._items.indexOf(item) !== -1;
};
ArraySet.prototype.add = function(item) {
if(!this.has(item)) {
this._items.push(item);
}
};
}
var makeSteal = function(System){
var addStealExtension = function (extensionFn) {
if (typeof System !== "undefined" && isFunction(extensionFn)) {
if (System._extensions) {
System._extensions.push(extensionFn);
}
extensionFn(System, steal);
}
};
System.set('@loader', System.newModule({
'default': System,
__useDefault: true
}));
System.set("less", System.newModule({
__useDefault: true,
default: {
fetch: function() {
throw new Error(
[
"steal-less plugin must be installed and configured properly",
"See https://stealjs.com/docs/steal-less.html"
].join("\n")
);
}
}
}));
System.config({
map: {
"@loader/@loader": "@loader",
"@steal/@steal": "@steal"
}
});
var configPromise,
devPromise,
appPromise;
var steal = function(){
var args = arguments;
var afterConfig = function(){
var imports = [];
var factory;
each(args, function(arg){
if(isString(arg)) {
imports.push( steal.System['import']( normalize(arg) ) );
} else if(typeof arg === "function") {
factory = arg;
}
});
var modules = Promise.all(imports);
if(factory) {
return modules.then(function(modules) {
return factory && factory.apply(null, modules);
});
} else {
return modules;
}
};
if(System.isEnv("production")) {
return afterConfig();
} else {
// wait until the config has loaded
return configPromise.then(afterConfig,afterConfig);
}
};
System.set("@steal", System.newModule({
"default": steal,
__useDefault:true
}));
System.Set = ArraySet;
var loaderClone = System.clone;
System.clone = function(){
var loader = loaderClone.apply(this, arguments);
loader.set("@loader", loader.newModule({
"default": loader,
__useDefault: true
}));
loader.set("@steal", loader.newModule({
"default": steal,
__useDefault: true
}));
loader.Set = ArraySet;
return loader;
};
// steal.System remains for backwards compat only
steal.System = steal.loader = System;
steal.parseURI = parseURI;
steal.joinURIs = joinURIs;
steal.normalize = normalize;
steal.relativeURI = relativeURI;
steal.addExtension = addStealExtension;
// System-Ext
// This normalize-hook does 2 things.
// 1. with specify a extension in your config
// you can use the "!" (bang) operator to load
// that file with the extension
// System.ext = {bar: "path/to/bar"}
// foo.bar! -> foo.bar!path/to/bar
// 2. if you load a javascript file e.g. require("./foo.js")
// normalize will remove the ".js" to load the module
addStealExtension(function addExt(loader) {
loader.ext = {};
var normalize = loader.normalize,
endingExtension = /\.(\w+)!?$/;
loader.normalize = function (name, parentName, parentAddress, pluginNormalize) {
if (pluginNormalize) {
return normalize.apply(this, arguments);
}
var matches = name.match(endingExtension);
var outName = name;
if (matches) {
var hasBang = name[name.length - 1] === "!",
ext = matches[1];
// load js-files nodd-like
if (parentName && loader.configMain !== name && matches[0] === '.js') {
outName = name.substr(0, name.lastIndexOf("."));
// matches ext mapping
} else if (loader.ext[ext]) {
outName = name + (hasBang ? "" : "!") + loader.ext[ext];
}
}
return normalize.call(this, outName, parentName, parentAddress);
};
});
// Steal Locate Extension
// normalize a given path e.g.
// "path/to/folder/" -> "path/to/folder/folder"
addStealExtension(function addForwardSlash(loader) {
var normalize = loader.normalize;
var npmLike = /@.+#.+/;
loader.normalize = function (name, parentName, parentAddress, pluginNormalize) {
var lastPos = name.length - 1,
secondToLast,
folderName,
newName = name;
if (name[lastPos] === "/") {
secondToLast = name.substring(0, lastPos).lastIndexOf("/");
folderName = name.substring(secondToLast + 1, lastPos);
if (npmLike.test(folderName)) {
folderName = folderName.substr(folderName.lastIndexOf("#") + 1);
}
newName += folderName;
}
return normalize.call(this, newName, parentName, parentAddress, pluginNormalize);
};
});
// override loader.translate to rewrite 'locate://' & 'pkg://' path schemes found
// in resources loaded by supporting plugins
addStealExtension(function addLocateProtocol(loader) {
/**
* @hide
* @function normalizeAndLocate
* @description Run a module identifier through Normalize and Locate hooks.
* @param {String} moduleName The module to run through normalize and locate.
* @return {Promise} A promise to resolve when the address is found.
*/
var normalizeAndLocate = function(moduleName, parentName){
var loader = this;
return Promise.resolve(loader.normalize(moduleName, parentName))
.then(function(name){
return loader.locate({name: name, metadata: {}});
}).then(function(address){
var outAddress = address;
if(address.substr(address.length - 3) === ".js") {
outAddress = address.substr(0, address.length - 3);
}
return outAddress;
});
};
var relative = function(base, path){
var uriParts = path.split("/"),
baseParts = base.split("/"),
result = [];
while ( uriParts.length && baseParts.length && uriParts[0] == baseParts[0] ) {
uriParts.shift();
baseParts.shift();
}
for(var i = 0 ; i< baseParts.length-1; i++) {
result.push("../");
}
return result.join("") + uriParts.join("/");
};
var schemePattern = /(locate):\/\/([a-z0-9/._@-]*)/ig,
parsePathSchemes = function(source, parent) {
var locations = [];
source.replace(schemePattern, function(whole, scheme, path, index){
locations.push({
start: index,
end: index+whole.length,
name: path,
postLocate: function(address){
return relative(parent, address);
}
});
});
return locations;
};
var _translate = loader.translate;
loader.translate = function(load){
var loader = this;
// This only applies to plugin resources.
if(!load.metadata.plugin) {
return _translate.call(this, load);
}
// Use the translator if this file path scheme is supported by the plugin
var locateSupport = load.metadata.plugin.locateScheme;
if(!locateSupport) {
return _translate.call(this, load);
}
// Parse array of module names
var locations = parsePathSchemes(load.source, load.address);
// no locations found
if(!locations.length) {
return _translate.call(this, load);
}
// normalize and locate all of the modules found and then replace those instances in the source.
var promises = [];
for(var i = 0, len = locations.length; i < len; i++) {
promises.push(
normalizeAndLocate.call(this, locations[i].name, load.name)
);
}
return Promise.all(promises).then(function(addresses){
for(var i = locations.length - 1; i >= 0; i--) {
load.source = load.source.substr(0, locations[i].start)
+ locations[i].postLocate(addresses[i])
+ load.source.substr(locations[i].end, load.source.length);
}
return _translate.call(loader, load);
});
};
});
addStealExtension(function addContextual(loader) {
loader._contextualModules = {};
loader.setContextual = function(moduleName, definer){
this._contextualModules[moduleName] = definer;
};
var normalize = loader.normalize;
loader.normalize = function(name, parentName){
var loader = this;
var pluginLoader = loader.pluginLoader || loader;
if (parentName) {
var definer = this._contextualModules[name];
// See if `name` is a contextual module
if (definer) {
var localName = name + '/' + parentName;
if(!loader.has(localName)) {
// `definer` could be a function or could be a moduleName
if (typeof definer === 'string') {
definer = pluginLoader['import'](definer);
}
return Promise.resolve(definer)
.then(function(modDefiner) {
var definer = modDefiner;
if (definer['default']) {
definer = definer['default'];
}
var definePromise = Promise.resolve(
definer.call(loader, parentName)
);
return definePromise;
})
.then(function(moduleDef){
loader.set(localName, loader.newModule(moduleDef));
return localName;
});
}
return Promise.resolve(localName);
}
}
return normalize.apply(this, arguments);
};
});
/**
* Steal Script-Module Extension
*
* Add a steal-module script to the page and it will run after Steal has been
* configured, e.g:
*
* <script type="text/steal-module">...</script>
* <script type="steal-module">...</script>
*/
addStealExtension(function addStealModule(loader) {
// taken from https://github.com/ModuleLoader/es6-module-loader/blob/master/src/module-tag.js
function completed() {
document.removeEventListener("DOMContentLoaded", completed, false);
window.removeEventListener("load", completed, false);
ready();
}
function ready() {
var scripts = document.getElementsByTagName("script");
for (var i = 0; i < scripts.length; i++) {
var script = scripts[i];
if (script.type == "steal-module" || script.type == "text/steal-module") {
var source = script.innerHTML;
if (/\S/.test(source)) {
loader.module(source)["catch"](function(err) {
setTimeout(function() {
throw err;
});
});
}
}
}
}
loader.loadScriptModules = function() {
if (isBrowserWithWindow) {
if (document.readyState === "complete") {
setTimeout(ready);
} else if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", completed, false);
window.addEventListener("load", completed, false);
}
}
};
});
// SystemJS Steal Format
// Provides the Steal module format definition.
addStealExtension(function addStealFormat(loader) {
// Steal Module Format Detection RegEx
// steal(module, ...)
var stealRegEx = /(?:^\s*|[}{\(\);,\n\?\&]\s*)steal\s*\(\s*((?:"[^"]+"\s*,|'[^']+'\s*,\s*)*)/;
// What we stole.
var stealInstantiateResult;
function createSteal(loader) {
stealInstantiateResult = null;
// ensure no NodeJS environment detection
loader.global.module = undefined;
loader.global.exports = undefined;
function steal() {
var deps = [];
var factory;
for( var i = 0; i < arguments.length; i++ ) {
if (typeof arguments[i] === 'string') {
deps.push( normalize(arguments[i]) );
} else {
factory = arguments[i];
}
}
if (typeof factory !== 'function') {
factory = (function(factory) {
return function() { return factory; };
})(factory);
}
stealInstantiateResult = {
deps: deps,
execute: function(require, exports, moduleName) {
var depValues = [];
for (var i = 0; i < deps.length; i++) {
depValues.push(require(deps[i]));
}
var output = factory.apply(loader.global, depValues);
if (typeof output !== 'undefined') {
return output;
}
}
};
}
loader.global.steal = steal;
}
var loaderInstantiate = loader.instantiate;
loader.instantiate = function(load) {
var loader = this;
if (load.metadata.format === 'steal' || !load.metadata.format && load.source.match(stealRegEx)) {
load.metadata.format = 'steal';
var oldSteal = loader.global.steal;
createSteal(loader);
loader.__exec(load);
loader.global.steal = oldSteal;
if (!stealInstantiateResult) {
throw "Steal module " + load.name + " did not call steal";
}
if (stealInstantiateResult) {
load.metadata.deps = load.metadata.deps ? load.metadata.deps.concat(stealInstantiateResult.deps) : stealInstantiateResult.deps;
load.metadata.execute = stealInstantiateResult.execute;
}
}
return loaderInstantiate.call(loader, load);
};
});
addStealExtension(function addMetaDeps(loader) {
var superTranspile = loader.transpile;
var superDetermineFormat = loader._determineFormat;
function prependDeps (loader, load, callback) {
var meta = loader.meta[load.name];
if (meta && meta.deps && meta.deps.length) {
var imports = meta.deps.map(callback).join('\n');
load.source = imports + "\n" + load.source;
}
}
function createImport(dep) {
return "import \"" + dep + "\";";
}
function createRequire(dep) {
return "require(\"" + dep + "\");";
}
loader.transpile = function (load) {
prependDeps(this, load, createImport);
var result = superTranspile.apply(this, arguments);
return result;
}
loader._determineFormat = function (load) {
if(load.metadata.format === 'cjs') {
prependDeps(this, load, createRequire);
}
var result = superDetermineFormat.apply(this, arguments);
return result;
};
});
addStealExtension(function addStackTrace(loader) {
function StackTrace(message, items) {
this.message = message;
this.items = items;
}
StackTrace.prototype.toString = function(){
var arr = ["Error: " + this.message];
var t, desc;
for(var i = 0, len = this.items.length; i < len; i++) {
t = this.items[i];
desc = " at ";
if(t.fnName) {
desc += (t.fnName + " ");
}
desc += StackTrace.positionLink(t);
arr.push(desc);
}
return arr.join("\n");
};
StackTrace.positionLink = function(t){
var line = t.line || 0;
var col = t.column || 0;
return "(" + t.url + ":" + line + ":" + col + ")";
};
StackTrace.item = function(fnName, url, line, column) {
return {
method: fnName,
fnName: fnName,
url: url,
line: line,
column: column
}
};
function parse(stack) {
var rawLines = stack.split('\n');
var v8Lines = compact(rawLines.map(parseV8Line));
if (v8Lines.length > 0) return v8Lines;
var geckoLines = compact(rawLines.map(parseGeckoLine));
if (geckoLines.length > 0) return geckoLines;
throw new Error('Unknown stack format: ' + stack);
}
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Stack
var GECKO_LINE = /^(?:([^@]*)@)?(.*?):(\d+)(?::(\d+))?$/;
function parseGeckoLine(line) {
var match = line.match(GECKO_LINE);
if (!match) return null;
var meth = match[1] || ''
return {
method: meth,
fnName: meth,
url: match[2] || '',
line: parseInt(match[3]) || 0,
column: parseInt(match[4]) || 0,
};
}
// https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
var V8_OUTER1 = /^\s*(eval )?at (.*) \((.*)\)$/;
var V8_OUTER2 = /^\s*at()() (\S+)$/;
var V8_INNER = /^\(?([^\(]+):(\d+):(\d+)\)?$/;
function parseV8Line(line) {
var outer = line.match(V8_OUTER1) || line.match(V8_OUTER2);
if (!outer) return null;
var inner = outer[3].match(V8_INNER);
if (!inner) return null;
var method = outer[2] || '';
if (outer[1]) method = 'eval at ' + method;
return {
method: method,
fnName: method,
url: inner[1] || '',
line: parseInt(inner[2]) || 0,
column: parseInt(inner[3]) || 0,
};
}
// Helpers
function compact(array) {
var result = [];
array.forEach(function(value) {
if (value) {
result.push(value);
}
});
return result;
}
StackTrace.parse = function(error) {
try {
var lines = parse(error.stack || error);
if(lines.length) {
return new StackTrace(error.message, lines);
}
} catch(e) {
return undefined;
}
};
loader.StackTrace = StackTrace;
function getPositionOfError(txt) {
var res = /at position ([0-9]+)/.exec(txt);
if(res && res.length > 1) {
return Number(res[1]);
}
}
loader.loadCodeFrame = function(){
if(!this.global.process) {
this.global.process = { argv: '', env: {} };
}
var loader = this.pluginLoader || this;
var isProd = loader.isEnv("production");
var p = isProd ? Promise.resolve() : loader["import"]("@@babel-code-frame");
return p;
};
loader._parseJSONError = function(err, source){
var pos = getPositionOfError(err.message);
if(pos) {
return this._getLineAndColumnFromPosition(source, pos);
} else {
return {line: 0, column: 0};
}
};
var errPos = /at position( |:)([0-9]+)/;
var errLine = /at line ([0-9]+) column ([0-9]+)/;
loader._parseSyntaxErrorLocation = function(error, load){
// V8 and Edge
var res = errPos.exec(error.message);
if(res && res.length === 3) {
var pos = Number(res[2]);
return this._getLineAndColumnFromPosition(load.source, pos);
}
// Firefox
res = errLine.exec(error.message);
if(res && res.length === 3) {
return {
line: Number(res[1]),
column: Number(res[2])
};
}
}
loader._addSourceInfoToError = function(err, pos, load, fnName){
return this.loadCodeFrame()
.then(function(codeFrame){
if(codeFrame) {
var src = load.metadata.originalSource || load.source;
var codeSample = codeFrame(src, pos.line, pos.column);
err.message += "\n\n" + codeSample + "\n";
}
var stackTrace = new StackTrace(err.message, [
StackTrace.item(fnName, load.address, pos.line, pos.column)
]);
err.stack = stackTrace.toString();
return Promise.reject(err);
});
};
function findStackFromAddress(st, address) {
for(var i = 0; i < st.items.length; i++) {
if(st.items[i].url === address) {
return st.items[i];
}
}
}
loader.rejectWithCodeFrame = function(error, load) {
var st = StackTrace.parse(error);
var item;
if(error.onlyIncludeCodeFrameIfRootModule) {
item = st && st.items[0] && st.items[0].url === load.address && st.items[0];
} else {
item = findStackFromAddress(st, load.address);
}
if(item) {
return this.loadCodeFrame()
.then(function(codeFrame){
if(codeFrame) {
var newError = new Error(error.message);
var line = item.line;
var column = item.column;
// CommonJS adds 3 function wrappers
if(load.metadata.format === "cjs") {
line = line - 3;
}
var src = load.metadata.originalSource || load.source;
var codeSample = codeFrame(src, line, column);
if(!codeSample) return Promise.reject(error);
newError.message += "\n\n" + codeSample + "\n";
st.message = newError.message;
newError.stack = st.toString();
return Promise.reject(newError);
} else {
return Promise.reject(error);
}
});
}
return Promise.reject(error);
};
});
addStealExtension(function addPrettyName(loader){
loader.prettyName = function(load){
var pnm = load.metadata.parsedModuleName;
if(pnm) {
return pnm.packageName + "/" + pnm.modulePath;
}
return load.name;
};
});
addStealExtension(function addTreeShaking(loader) {
function treeShakingEnabled(loader, load) {
return !loader.noTreeShaking && loader.treeShaking !== false;
}
function determineUsedExports(load) {
var loader = this;
// 1. Get any new dependencies that haven't been accounted for.
var newDeps = newDependants.call(this, load);
var usedExports = new loader.Set();
var allUsed = false;
newDeps.forEach(function(depName) {
var depLoad = loader.getModuleLoad(depName);
var specifier = loader.moduleSpecifierFromName(depLoad, load.name);
if (depLoad.metadata.format !== "es6") {
allUsed = true;
return;
}
});
// Only walk the export tree if all are not being used.
// This saves not needing to do the traversal.
if(!allUsed) {
allUsed = walkExports.call(loader, load, function(exps){
exps.forEach(function(name){
usedExports.add(name);
});
});
}
// Copy over existing exports
if(load.metadata.usedExports) {
load.metadata.usedExports.forEach(function(name){
usedExports.add(name);
});
}
if(!loader.treeShakeConfig[load.name]) {
loader.treeShakeConfig[load.name] = Object.create(null);
}
load.metadata.usedExports = loader.treeShakeConfig[load.name].usedExports = usedExports;
load.metadata.allExportsUsed = loader.treeShakeConfig[load.name].allExportsUsed = allUsed;
return {
all: allUsed,
used: usedExports
};
}
// Determine if this load's dependants have changed,
function newDependants(load) {
var out = [];
var deps = this.getDependants(load.name);
var shakenParents = load.metadata.shakenParents;
if (!shakenParents) {
out = deps;
} else {
for (var i = 0; i < deps.length; i++) {
if (shakenParents.indexOf(deps[i]) === -1) {
out.push(deps[i]);
}
}
}
return out;
}
function walkExports(load, cb) {
var moduleName = load.name;
var name = moduleName;
var visited = new this.Set();
// The stack is an array containing stuff we are traversing.
// It looks like:
// [moduleName, parentA, parentB, null]
var stack = [name].concat(this.getDependants(name));
var namesMap = null;
var index = 0;
var cont = true;
// If there is only one item in the stack, this module has no parents yet.
if(stack.length === 1) {
return true;
}
// Special case for immediate parents, as these are the ones
// That determine when all exports are used.
var immediateParents = Object.create(null);
stack.forEach(function(name) {
immediateParents[name] = true;
});
do {
index++;
var parentName = stack[index];
if(parentName == null) {
name = stack[++index];
cont = index < stack.length - 1;
continue;
}
if(visited.has(parentName)) {
continue;
}
visited.add(parentName);
var parentLoad = this.getModuleLoad(parentName);
var parentSpecifier = this.moduleSpecifierFromName(
parentLoad,
name
);
var parentIsESModule = parentLoad.metadata.format === "es6";
var parentImportNames = parentLoad.metadata.importNames;
var parentExportNames = parentLoad.metadata.exportNames;
// If this isn't an ES module then return true (indicating all are used)
if(!parentIsESModule && immediateParents[parentName]) {
return true;
}
if(parentImportNames && parentImportNames[parentSpecifier]) {
var names = parentImportNames[parentSpecifier];
if(namesMap) {
var parentsNames = names;
names = [];
parentsNames.forEach(function(name){
if(namesMap.has(name)) {
names.push(namesMap.get(name));
}
});
}
cont = cb(names) !== false;
}
if(parentExportNames && parentExportNames[parentSpecifier]) {
var names = parentExportNames[parentSpecifier];
var parentDependants = this.getDependants(parentName);
// Named exports
if(isNaN(names)) {
namesMap = names;
}
// export * with no dependants should result in no tree-shaking
else if(!parentDependants.length) {
return true;
}
stack.push(null);
stack.push(parentName);
stack.push.apply(stack, parentDependants);
}
cont = cont !== false && index < stack.length - 1;
} while(cont);
return false;
}
/**
* Determine if the new parent has resulted in new used export names
* If so, redefine this module so that it goes into the registry correctly.
*/
function reexecuteIfNecessary(load, parentName) {
var usedExports = [];
var allExportsUsed = walkExports.call(this, load, function(exps) {
usedExports.push.apply(usedExports, exps);
});
// Given the parent's used exports, loop over and see if any are not
// within the usedExports set.
var hasNewExports = allExportsUsed;
// If there isn't a usedExports Set, we have yet to check.
if(!allExportsUsed && load.metadata.usedExports) {
for (var i = 0; i < usedExports.length; i++) {
if (!load.metadata.usedExports.has(usedExports[i])) {
hasNewExports = true;
break;
}
}
}
if (hasNewExports) {
var source = load.metadata.originalSource || load.source;
this.provide(load.name, source, load);
}
return Promise.resolve();
}
// Check if a module has already been tree-shaken.
// And if so, re-execute it if there are new dependant modules.
var notifyLoad = loader.notifyLoad;
loader.notifyLoad = function(specifier, name, parentName){
var load = loader.getModuleLoad(name);
// If this module is already marked as tree-shakable it means
// it has been loaded before. Determine if it needs to be reexecuted.
if (load && load.metadata.treeShakable) {
return reexecuteIfNecessary.call(this, load, parentName);
}
return notifyLoad.apply(this, arguments);
};
function treeShakePlugin(loader, load) {
// existence of this type of Node means the module is not tree-shakable
var notShakable = {
exit: function(path, state) {
state.treeShakable = false;
}
};
// "bare" imports like `import "foo";` do not affect tree-shaking
// any non-"bare" import means module cannot be tree-shaken
var checkImportForShakability = {
exit: function(path, state) {
state.treeShakable = path.node.specifiers.length === 0;
}
};
var notShakeableVisitors = {
ImportDeclaration: checkImportForShakability,
FunctionDeclaration: notShakable,
VariableDeclaration: notShakable
};
var usedResult;
// Call determineUsedExports, caching the result.
function _determineUsedExports() {
if(usedResult) {
return usedResult;
}
usedResult = determineUsedExports.call(
loader,
load
);
return usedResult;
}
return {
visitor: {
Program: {
enter: function(path) {
var state = {};
path.traverse(notShakeableVisitors, state);
load.metadata.treeShakable = state.treeShakable !== false;
if(!loader.treeShakeConfig[load.name]) {
loader.treeShakeConfig[load.name] = Object.create(null);
}
loader.treeShakeConfig[load.name].treeShakable = load.metadata.treeShakable;
}
},
ExportNamedDeclaration: function(path, state) {
if (load.metadata.treeShakable) {
var usedResult = _determineUsedExports();
var usedExports = usedResult.used;
var allUsed = usedResult.all;
if (!allUsed) {
path.get("specifiers").forEach(function(path) {
var name = path.get("exported.name").node;
if (
!usedExports.has(name) &&
name !== "__esModule"
) {
path.remove();
}
});
if (path.get("specifiers").length === 0) {
path.remove();
}
}
}
},
ExportAllDeclaration: function(path, state) {
if(load.metadata.treeShakable) {
// This forces the load.metadata.usedExports property to be set
// This is needed in modules that *only* have `export *` declarations.
_determineUsedExports();
}
}
}
};
}
// Collect syntax plugins, because we need to always include these.
var getSyntaxPlugins = (function(){
var plugins;
return function(babel) {
if(!plugins) {
plugins = [];
for(var p in babel.availablePlugins) {
if(p.indexOf("syntax-") === 0) {
plugins.push(babel.availablePlugins[p]);
}
}
}
return plugins;
};
})();
function applyBabelPlugin(load) {
var loader = this;
var pluginLoader = loader.pluginLoader || loader;
return pluginLoader.import("babel").then(function(mod) {
var transpiler = mod.__useDefault ? mod.default : mod;
var babel = transpiler.Babel || transpiler.babel || transpiler;
try {
var babelPlugins = [].concat(getSyntaxPlugins(babel));
babelPlugins.push(loader._getImportSpecifierPositionsPlugin.bind(null, load));
if(treeShakingEnabled(loader, load)) {
babelPlugins.push(treeShakePlugin.bind(null, loader, load));
}
var code = babel.transform(load.source, {
plugins: babelPlugins,
compact: false,
filename: load && load.address
}).code;
// If everything is tree shaken still mark as ES6
// Not doing this and steal won't accept the transform.
if(code === "") {
return '"format es6";';
}
return code;
} catch (e) {
// Probably using some syntax that requires additional plugins.
if(e instanceof SyntaxError) {
return Promise.resolve();
}
return Promise.reject(e);
}
});
}
var translate = loader.translate;
var es6RegEx = /(^\s*|[}\);\n]\s*)(import\s+(['"]|(\*\s+as\s+)?[^"'\(\)\n;]+\s+from\s+['"]|\{)|export\s+\*\s+from\s+["']|export\s+(\{|default|function|class|var|const|let|async\s+function))/;
loader.translate = function treeshakeTranslate(load) {
var loader = this;
return Promise.resolve()
.then(function() {
if (es6RegEx.test(load.source)) {
if(!load.metadata.originalSource)
load.metadata.originalSource = load.source;
return applyBabelPlugin.call(loader, load);
}
})
.then(function(source) {
if (source) {
load.source = source;
}
return translate.call(loader, load);
});
};
// For the build, wrap the _newLoader hook. This is to copy config over
// that needs to exist for all loaders.
loader.treeShakeConfig = Object.create(null);
var newLoader = loader._newLoader || Function.prototype;
loader._newLoader = function(loader){
var treeShakeConfig = this.treeShakeConfig;
loader.treeShakeConfig = this.treeShakeConfig;
for(var moduleName in treeShakeConfig) {
var moduleTreeShakeConfig = treeShakeConfig[moduleName];
var metaConfig = Object.create(null);
metaConfig.treeShakable = moduleTreeShakeConfig.treeShakable;
metaConfig.usedExports = new this.Set(moduleTreeShakeConfig.usedExports);
metaConfig.allExportsUsed = moduleTreeShakeConfig.allExportsUsed;
var config = {meta:{}};
config.meta[moduleName] = metaConfig;
loader.config(config);
}
};
});
addStealExtension(function addMJS(loader){
var mjsExp = /\.mjs$/;
var jsExp = /\.js$/;
var locate = loader.locate;
loader.locate = function(load){
var isMJS = mjsExp.test(load.name);
var p = locate.apply(this, arguments);
if(isMJS) {
return Promise.resolve(p).then(function(address) {
if(jsExp.test(address)) {
return address.substr(0, address.length - 3);
}
return address;
});
}
return p;
};
});
addStealExtension(function applyTraceExtension(loader) {
loader._traceData = {
loads: {},
parentMap: {}
};
loader.getDependencies = function(moduleName){
var load = this.getModuleLoad(moduleName);
return load ? load.metadata.dependencies : undefined;
};
loader.getDependants = function(moduleName){
var deps = [];
var pars = this._traceData.parentMap[moduleName] || {};
eachOf(pars, function(name) { deps.push(name); });
return deps;
};
loader.getModuleLoad = function(moduleName){
return this._traceData.loads[moduleName];
};
loader.getBundles = function(moduleName, argVisited){
var visited = argVisited || {};
visited[moduleName] = true;
var loader = this;
var parentMap = loader._traceData.parentMap;
var parents = parentMap[moduleName];
if(!parents) return [moduleName];
var bundles = [];
eachOf(parents, function(parentName, value){
if(!visited[parentName])
bundles = bundles.concat(loader.getBundles(parentName, visited));
});
return bundles;
};
loader.getImportSpecifier = function(fullModuleName, load){
var idx = 0, specifier;
while(idx < load.metadata.dependencies.length) {
if(load.metadata.dependencies[idx] === fullModuleName) {
specifier = load.metadata.deps[idx];
break;
}
idx++;
}
if(specifier) {
if(load.metadata.importSpecifiers) {
return (load.metadata.importSpecifiers[specifier] || {}).start;
} else if(load.metadata.getImportPosition) {
return load.metadata.getImportPosition(specifier);
}
}
};
loader.moduleSpecifierFromName = function(load, moduleName) {
var deps = load.metadata.dependencies;
if(!deps) return undefined;
var idx = deps.indexOf(moduleName);
return load.metadata.deps[idx];
};
loader._allowModuleExecution = {};
loader.allowModuleExecution = function(name){
var loader = this;
return loader.normalize(name).then(function(name){
loader._allowModuleExecution[name] = true;
});
};
function eachOf(obj, callback){
var name, val;
for(name in obj) {
callback(name, obj[name]);
}
}
var normalize = loader.normalize;
loader.normalize = function(name, parentName){
var normalizePromise = normalize.apply(this, arguments);
if(parentName) {
var parentMap = this._traceData.parentMap;
return normalizePromise.then(function(name){
if(!parentMap[name]) {
parentMap[name] = {};
}
parentMap[name][parentName] = true;
return name;
});
}
return normalizePromise;
};
var emptyExecute = function(){
return loader.newModule({});
};
var passThroughModules = {
traceur: true,
babel: true
};
var isAllowedToExecute = function(load){
return passThroughModules[load.name] || this._allowModuleExecution[load.name];
};
var map = [].map || function(callback){
var res = [];
for(var i = 0, len = this.length; i < len; i++) {
res.push(callback(this[i]));
}
return res;
};
var esImportDepsExp = /import [\s\S]*?["'](.+)["']/g;
var esExportDepsExp = /export .+ from ["'](.+)["']/g;
var commentRegEx = /(?:(?:^|\s)\/\/(.+?)$)|(?:\/\*([\S\s]*?)\*\/)/gm;
var stringRegEx = /(?:("|')[^\1\\\n\r]*(?:\\.[^\1\\\n\r]*)*\1|`[^`]*`)/g;
function getESDeps(source) {
var cleanSource = source.replace(commentRegEx, "");
esImportDepsExp.lastIndex = commentRegEx.lastIndex =
esExportDepsExp.lastIndex = stringRegEx.lastIndex = 0;
var match;
var deps = [];
var stringLocations = []; // track string for unminified source
function inLocation(locations, match) {
for (var i = 0; i < locations.length; i++)
if (locations[i][0] < match.index && locations[i][1] > match.index)
return true;
return false;
}
function addDeps(exp) {
while (match = exp.exec(cleanSource)) {
// ensure we're not within a string location
if (!inLocation(stringLocations, match)) {
var dep = match[1];
deps.push(dep);
}
}
}
if (source.length / source.split('\n').length < 200) {
while (match = stringRegEx.exec(cleanSource))
stringLocations.push([match.index, match.index + match[0].length]);
}
addDeps(esImportDepsExp);
addDeps(esExportDepsExp);
return deps;
}
var instantiate = loader.instantiate;
loader.instantiate = function(load){
this._traceData.loads[load.name] = load;
var loader = this;
var instantiatePromise = Promise.resolve(instantiate.apply(this, arguments));
function finalizeResult(result){
var preventExecution = loader.preventModuleExecution &&
!isAllowedToExecute.call(loader, load);
// deps either comes from the instantiate result, or if an
// es6 module it was found in the transpile hook.
var deps = result ? result.deps : load.metadata.deps;
var normalize = loader.normalizeSpecifier || loader.normalize;
return Promise.all(map.call(deps, function(depName){
return normalize.call(loader, depName, load.name);
})).then(function(dependencies){
load.metadata.deps = deps;
load.metadata.dependencies = dependencies;
if(preventExecution) {
return {
deps: deps,
execute: emptyExecute
};
}
return result;
});
}
return instantiatePromise.then(function(result){
// This must be es6
if(!result) {
var deps = getESDeps(load.source);
load.metadata.deps = deps;
}
return finalizeResult(result);
});
};
var transpile = loader.transpile;
// Allow transpile to be memoized, but only once
loader.transpile = function(load){
var transpiled = load.metadata.transpiledSource;
if(transpiled) {
delete load.metadata.transpiledSource;
return Promise.resolve(transpiled);
}
return transpile.apply(this, arguments);
};
loader.eachModule = function(cb){
for (var moduleName in this._loader.modules) {
cb.call(this, moduleName, this.get(moduleName));
}
};
});
// Steal JSON Format
// Provides the JSON module format definition.
addStealExtension(function addJSON(loader) {
var jsonExt = /\.json$/i;
var jsExt = /\.js$/i;
// taken from prototypejs
// https://github.com/sstephenson/prototype/blob/master/src/prototype/lang/string.js#L682-L706
function isJSON(json) {
var str = json;
if (!str) return false;
str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');
str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
return (/^[\],:{}\s]*$/).test(str);
}
// if someone has a moduleName that is .json, make sure it loads a json file
// no matter what paths might do
var loaderLocate = loader.locate;
loader.locate = function(load){
return loaderLocate.apply(this, arguments).then(function(address){
if(jsonExt.test(load.name)) {
return address.replace(jsExt, "");
}
return address;
});
};
var transform = function(loader, load, data){
var fn = loader.jsonOptions && loader.jsonOptions.transform;
if(!fn) return data;
return fn.call(loader, load, data);
};
// If we are in a build we should convert to CommonJS instead.
if(isNode) {
var loaderTranslate = loader.translate;
loader.translate = function(load){
var address = load.metadata.address || load.address;
if(jsonExt.test(address) && load.name.indexOf('!') === -1) {
var parsed = parse.call(this, load);
if(parsed) {
parsed = transform(this, load, parsed);
return "def" + "ine([], function(){\n" +
"\treturn " + JSON.stringify(parsed) + "\n});";
}
}
return loaderTranslate.call(this, load);
};
return;
}
var loaderInstantiate = loader.instantiate;
loader.instantiate = function(load) {
var loader = this,
parsed;
parsed = parse.call(this, load);
if(parsed) {
parsed = transform(loader, load, parsed);
load.metadata.format = 'json';
load.metadata.execute = function(){
return parsed;
};
}
return loaderInstantiate.call(loader, load);
};
return loader;
// Attempt to parse a load as json.
function parse(load){
if ((load.metadata.format === 'json' || !load.metadata.format) &&
(isJSON(load.source) || jsonExt.test(load.name))) {
try {
return JSON.parse(load.source);
} catch(e) {
var warn = console.warn.bind(console);
if(e instanceof SyntaxError) {
var loc = this._parseSyntaxErrorLocation(e, load);
if(loc) {
var msg = "Unable to parse " + load.address;
var newError = new SyntaxError(msg);
newError.promise = this._addSourceInfoToError(newError,
loc, load, "JSON.parse");
throw newError;
}
}
warn("Error parsing " + load.address + ":", e);
return {};
}
}
}
});
// Steal Cache-Bust Extension
// if enabled, Steal Cache-Bust will add a
// cacheKey and cacheVersion to the required file address
addStealExtension(function addCacheBust(loader) {
var fetch = loader.fetch;
loader.fetch = function(load) {
var loader = this;
if(loader.isEnv("production") && loader.cacheVersion) {
var cacheVersion = loader.cacheVersion,
cacheKey = loader.cacheKey || "version",
cacheKeyVersion = cacheKey + "=" + cacheVersion;
load.address = load.address + (load.address.indexOf('?') === -1 ? '?' : '&') + cacheKeyVersion;
}
return fetch.call(this, load);
};
});
// Overwrites System.config with setter hooks
var setterConfig = function(loader, configOrder, configSpecial){
var oldConfig = loader.config;
loader.config = function(cfg){
var data = extend({},cfg);
// check each special
each(configOrder, function(name){
var special = configSpecial[name];
// if there is a setter and a value
if(special.set && data[name]){
// call the setter
var res = special.set.call(loader,data[name], cfg);
// if the setter returns a value
if(res !== undefined) {
// set that on the loader
loader[name] = res;
}
// delete the property b/c setting is done
delete data[name];
}
});
oldConfig.call(this, data);
};
};
var setIfNotPresent = function(obj, prop, value){
if(!obj[prop]) {
obj[prop] = value;
}
};
// steal.js's default configuration values
System.configMain = "@config";
System.devBundle = "@empty";
System.depsBundle = "@empty";
System.paths[System.configMain] = "stealconfig.js";
System.env = (isWebWorker ? "worker" : "window") + "-development";
System.ext = Object.create(null);
System.logLevel = 0;
System.forceES5 = true;
var cssBundlesNameGlob = "bundles/*.css",
jsBundlesNameGlob = "bundles/*";
setIfNotPresent(System.paths,cssBundlesNameGlob, "dist/bundles/*css");
setIfNotPresent(System.paths,jsBundlesNameGlob, "dist/bundles/*.js");
var less = System.global.less || (System.global.less = {});
less.async = true;
var configSetter = function(order){
return {
order: order,
set: function(val){
var name = filename(val),
root = dir(val);
if(!isNode) {
System.configPath = joinURIs( location.href, val);
}
System.configMain = name;
System.paths[name] = name;
this.config({ baseURL: (root === val ? "." : root) + "/" });
}
}
},
valueSetter = function(prop, order) {
return {
order: order,
set: function(val) {
this[prop] = val;
}
}
},
booleanSetter = function(prop, order) {
return {
order: order,
set: function(val) {
this[prop] = !!val && val !== "false";
}
}
},
fileSetter = function(prop, order) {
return {
order: order,
set: function(val) {
this[prop] = envPath(val);
}
};
};
// checks if we're running in node, then prepends the "file:" protocol if we are
var envPath = function(pathVal) {
var val = pathVal;
if(isNode && !/^file:/.test(val)) {
// If relative join with the current working directory
if(val[0] === "." && (val[1] === "/" ||
(val[1] === "." && val[2] === "/"))) {
val = require("path").join(process.cwd(), val);
}
if(!val) return val;
return "file:" + val;
}
return val;
};
var setToSystem = function(prop){
return {
set: function(val){
if(typeof val === "object" && typeof steal.System[prop] === "object") {
this[prop] = extend(this[prop] || {},val || {});
} else {
this[prop] = val;
}
}
};
};
var pluginPart = function(name) {
var bang = name.lastIndexOf("!");
if(bang !== -1) {
return name.substr(bang+1);
}
};
var pluginResource = function(name){
var bang = name.lastIndexOf("!");
if(bang !== -1) {
return name.substr(0, bang);
}
};
var addProductionBundles = function(){
// we don't want add the main bundled module if steal is bundled inside!
if(this.loadBundles && this.main && !this.stealBundled) {
var main = this.main,
bundlesDir = this.bundlesName || "bundles/",
mainBundleName = bundlesDir+main;
setIfNotPresent(this.meta, mainBundleName, {format:"amd"});
// If the configMain has a plugin like package.json!npm,
// plugin has to be defined prior to importing.
var plugin = pluginPart(System.configMain);
var bundle = [main, System.configMain];
if(plugin){
System.set(plug