UNPKG

steal

Version:
1,842 lines (1,594 loc) 142 kB
(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