UNPKG

steal

Version:
588 lines (511 loc) 17.4 kB
"format cjs"; var steal = require("@steal"); var utils = require("./npm-utils"); exports.includeInBuild = true; var isNode = typeof process === "object" && {}.toString.call(process) === "[object process]"; var isWorker = typeof WorkerGlobalScope !== "undefined" && (self instanceof WorkerGlobalScope); var isElectron = isNode && !!process.versions.electron; var isBrowser = typeof window !== "undefined" && (!isNode || isElectron) && !isWorker; exports.addExtension = function addNpmExtension(System){ if (System._extensions) { System._extensions.push(addNpmExtension); } // Returns a replacement identifier if there is one in `.importRewrites` // Arguments: // - identifier - the identifier to be replaced // - parsedParentName - parent name of the module that is doing the importing // Returns `undefined` if there's no match, a string replacement if one is found function getImportRewritesValue(identifier, parsedParentName) { if(this.importRewrites && typeof this.importRewrites === "object" && parsedParentName) { var childimportRewrites = this.importRewrites[parsedParentName.packageName] if(childimportRewrites) { var identifierValue = childimportRewrites[identifier]; return identifierValue; } } } /** * Normalize has to deal with a "tricky" situation. There are module names like * "css" -> "css" normalize like normal * "./qunit" //-> "qunit" ... could go to steal-qunit#qunit, but then everything would? * * isRoot? * "can-slider" //-> "path/to/main" * * else * * "can-slider" //-> "can-slider#path/to/main" */ var oldNormalize = System.normalize; System.normalize = function npmNormalize(identifier, parentModuleName, parentAddress, pluginNormalize){ var name = identifier; var parentName = parentModuleName; if(parentName && this.npmParentMap && this.npmParentMap[parentName]) { parentName = this.npmParentMap[parentName]; } var hasNoParent = !parentName; var parsedParentModuleName = parentName && utils.moduleName.parse(parentName); var parentIsNpmModule = utils.moduleName.isNpm(parentName); var importRewritesValue = getImportRewritesValue.call(this, identifier, parsedParentModuleName); if(importRewritesValue !== undefined) { name = importRewritesValue; } var nameIsRelative = utils.path.isRelative(name); var nameIsNpmModule = utils.moduleName.isNpm(name); var identifierEndsWithSlash = utils.path.endsWithSlash(name); // If this is an npm module name already, we don't need to re-resolve it. if(nameIsNpmModule && parentModuleName) { return oldNormalize.call(this, name, parentName, parentAddress, pluginNormalize); } // If this is a relative module name and the parent is not an npm module // we can skip all of this logic. if(parentName && nameIsRelative && !parentIsNpmModule) { return oldNormalize.call(this, name, parentName, parentAddress, pluginNormalize); } // don't normalize name module yet if it includes any conditionals if (utils.moduleName.isConditional(name)) { return oldNormalize.call(this, name, parentName, parentAddress, pluginNormalize); } // Check against contextual maps that would not be converted. var hasContextualMap = typeof this.map[parentName] === "object" && this.map[parentName][name]; if(hasContextualMap) { return oldNormalize.call(this, name, parentName, parentAddress, pluginNormalize); } // Get the current package var refPkg = utils.pkg.findByModuleNameOrAddress(this, parentName, parentAddress); // this isn't in a package, so ignore if(!refPkg) { return oldNormalize.call(this, name, parentName, parentAddress, pluginNormalize); } // If name is ./ or ../ var isPointingAtParentFolder = name === "../" || name === "./"; if(parentIsNpmModule && isPointingAtParentFolder) { var parentModulePath = parsedParentModuleName.modulePath || ""; var relativePath = utils.path.relativeTo(parentModulePath, name); var isInRoot = utils.path.isPackageRootDir(relativePath); if(isInRoot) { name = refPkg.name + "#" + utils.path.removeJS( utils.path.removeDotSlash(refPkg.main) ); } else { name = name + "index"; } } // Using the current package, get info about what it is probably asking for var parsedModuleName = utils.moduleName.parseFromPackage(this, refPkg, name, parentName); var isRoot = utils.pkg.isRoot(this, refPkg); var parsedPackageNameIsReferringPackage = parsedModuleName.packageName === refPkg.name; // Are we normalizing a module that is relative to another npm module? var isRelativeToParentNpmModule = parentIsNpmModule && nameIsRelative && parsedPackageNameIsReferringPackage; // Look for the dependency package specified by the current package var depPkg, wantedPkg; // If we are within the same package then refPkg is the package we care // about if(isRelativeToParentNpmModule) { depPkg = refPkg; } var context = this.npmContext; var crawl = context && context.crawl; var isDev = !!crawl; if(!depPkg) { // Development mode if(crawl) { var parentPkg = nameIsRelative ? null : crawl.matchedVersion(context, refPkg.name, refPkg.version); if(parentPkg) { var depMap = crawl.getFullDependencyMap(this, parentPkg, isRoot); wantedPkg = depMap[parsedModuleName.packageName]; if(wantedPkg) { var wantedVersion = (refPkg.resolutions && refPkg.resolutions[wantedPkg.name]) || wantedPkg.version; var foundPkg = crawl.matchedVersion(this.npmContext, wantedPkg.name, wantedVersion); if(foundPkg) { depPkg = utils.pkg.findByUrl(this, foundPkg.fileUrl); } } } } else { if(isRoot) { depPkg = utils.pkg.findDepWalking(this, refPkg, parsedModuleName.packageName); } else { depPkg = utils.pkg.findDep(this, refPkg, parsedModuleName.packageName); } } } // If the parent package is loading itself by name, look up by version if(parsedPackageNameIsReferringPackage) { depPkg = utils.pkg.findByNameAndVersion(this, parsedModuleName.packageName, refPkg.version); } // This really shouldn't happen, but lets find a package. var lookupByName = parsedModuleName.isGlobal || hasNoParent; if (!depPkg) { depPkg = utils.pkg.findByName(this, parsedModuleName.packageName); } var isThePackageWeWant = !isDev || !depPkg || (wantedPkg ? crawl.pkgSatisfies(depPkg, wantedPkg.version) : true); if(!isThePackageWeWant) { depPkg = undefined; } else if(isDev && depPkg) { utils.pkg.saveResolution(context, refPkg, depPkg); } // It could be something like `fs` so check in globals if(!depPkg) { var browserPackageName = this.globalBrowser[parsedModuleName.packageName]; if(browserPackageName) { parsedModuleName.packageName = browserPackageName.moduleName; depPkg = utils.pkg.findByName(this, parsedModuleName.packageName); } } // It could be the root main. if(!depPkg && isRoot && name === refPkg.main && utils.pkg.hasDirectoriesLib(refPkg)) { parsedModuleName.version = refPkg.version; parsedModuleName.packageName = refPkg.name; parsedModuleName.modulePath = utils.pkg.main(refPkg); return oldNormalize.call(this, utils.moduleName.create(parsedModuleName), parentName, parentAddress, pluginNormalize); } // This is the beginning of progressively loading package.json var loader = this; if(!depPkg) { if(crawl) { var parentPkg = crawl.matchedVersion(this.npmContext, refPkg.name, refPkg.version); if(parentPkg) { var depMap = crawl.getFullDependencyMap(this, parentPkg, isRoot); depPkg = depMap[parsedModuleName.packageName]; // If we still haven't found a package, try to find it by // name, we'll take whatever we can get. if(!depPkg) { var parents = crawl.findPackageAndParents(this.npmContext, parsedModuleName.packageName); if(parents) { depPkg = parents.package; } } } } if(!depPkg) { if(refPkg.browser && refPkg.browser[name]) { return oldNormalize.call(this, refPkg.browser[name], parentName, parentAddress, pluginNormalize); } // Apply mappings, if they exist in the refPkg var steal = utils.pkg.config(refPkg); if (steal && steal.map && typeof steal.map[name] === "string") { var mappedName = steal.map[name]; var envConfig = steal.envs && steal.envs[loader.env]; if(envConfig && envConfig.map && typeof envConfig.map[name] === "string") { mappedName = envConfig.map[name]; } return loader.normalize( mappedName, parentName, parentAddress, pluginNormalize ); } else { return oldNormalize.call( this, name, parentName, parentAddress, pluginNormalize ); } } return crawl.dep(this.npmContext, parentPkg, refPkg, depPkg, isRoot) .then(createModuleNameAndNormalize); } else { return createModuleNameAndNormalize(depPkg); } function createModuleNameAndNormalize(depPkg){ parsedModuleName.version = depPkg.version; // add the main path if(!parsedModuleName.modulePath) { parsedModuleName.modulePath = utils.pkg.main(depPkg); } var p = oldNormalize.call( loader, utils.moduleName.create(parsedModuleName), parentName, parentAddress, pluginNormalize ); // For identifiers like ./lib/ save this info as we might // get a 404 and need to retry with lib/index.js if(identifierEndsWithSlash) { p.then(function(name){ if(context && context.forwardSlashMap) { context.forwardSlashMap[name] = true; } }); } return p; } }; var oldLocate = System.locate; System.locate = function(load){ var parsedModuleName = utils.moduleName.parse(load.name), loader = this; var pmn = load.metadata.parsedModuleName = parsedModuleName; load.metadata.npmPackage = utils.pkg.findByNameAndVersion(this, pmn.packageName, pmn.version); // @ is not the first character if(parsedModuleName.version && this.npm && !loader.paths[load.name]) { var pkg = this.npm[utils.moduleName.nameAndVersion(parsedModuleName)]; if(pkg) { return oldLocate.call(this, load).then(function(locatedAddress){ var address = locatedAddress; var expectedAddress = utils.path.joinURIs( System.baseURL, load.name ); if(isBrowser) { expectedAddress = expectedAddress.replace(/#/g, "%23"); } // If locate didn't do the expected thing then we're going // to guess that we shouldn't perform npm lookup on this // module as there might be a wildcard path. if(address !== expectedAddress + ".js" && address !== expectedAddress) { return address; } var root = utils.pkg.rootDir(pkg, utils.pkg.isRoot(loader, pkg)); if(parsedModuleName.modulePath) { var npmAddress = utils.path.joinURIs( utils.path.addEndingSlash(root), parsedModuleName.plugin ? parsedModuleName.modulePath : utils.path.addJS(parsedModuleName.modulePath) ); address = typeof steal !== "undefined" ? utils.path.joinURIs(loader.baseURL, npmAddress) : npmAddress; } return address; }); } } return oldLocate.call(this, load); }; var oldFetch = System.fetch; System.fetch = function(load){ if(load.metadata.dryRun) { return oldFetch.apply(this, arguments); } var loader = this; var context = loader.npmContext; var fetchPromise = Promise.resolve(oldFetch.apply(this, arguments)); if(utils.moduleName.isNpm(load.name)) { fetchPromise = fetchPromise.then(null, function(err){ var statusCode = err.statusCode; if(statusCode !== 404 && statusCode !== 0) { return Promise.reject(err); } // In order to retry in dev bundles there must be a forwardSlashMap // to maintain the state. if(!loader.npmContext) { loader.npmContext = { forwardSlashMap: {} }; } // Begin attempting retries. `retryTypes` defines different // types of retries to do, currently retrying on the // /index and /package.json conventions. var types = [].slice.call(retryTypes); return retryAll(types, err).then(null, function(e){ // Return the original error. return Promise.reject(err); }); function retryAll(types, err){ if(!types.length) { throw err; } var type = types.shift(); if(!type.test(load)) { throw err; } return Promise.resolve(retryFetch.call(loader, load, type)) .then(null, function(err){ return retryAll(types, err); }); } }); } return fetchPromise.catch(function(error) { var statusCode = error.statusCode; if ((statusCode === 404 || statusCode === 0) && utils.moduleName.isBareIdentifier(load.name) && !utils.pkg.isRoot(loader, load.metadata.npmPackage)) { var newError = new Error([ "Could not load '" + load.name + "'", "Is this an npm module not saved in your package.json?" ].join("\n")); newError.statusCode = error.statusCode; newError.stack = newError.stack + error.stack; throw newError; } else { throw error; } }); }; // Given a moduleName convert it into a npm-style moduleName if it belongs // to a package. var convertName = function(loader, name){ var pkg = utils.pkg.findByName(loader, name.split("/")[0]); if(pkg) { var parsed = utils.moduleName.parse(name, pkg.name); parsed.version = pkg.version; if(!parsed.modulePath) { parsed.modulePath = utils.pkg.main(pkg); } return utils.moduleName.create(parsed); } return name; }; var configSpecial = { map: function(map){ var newMap = {}, val; for(var name in map) { val = map[name]; newMap[convertName(this, name)] = typeof val === "object" ? configSpecial.map(val) : convertName(this, val); } return newMap; }, meta: function(map){ var newMap = {}; for(var name in map){ newMap[convertName(this, name)] = map[name]; } return newMap; }, paths: function(paths){ var newPaths = {}; for(var name in paths) { newPaths[convertName(this, name)] = paths[name]; } return newPaths; }, importRewrites: function(importRewrites) { return importRewrites; } }; var oldConfig = System.config; System.config = function(cfg){ var loader = this; // Use npm-convert if it is available as it is better // and has the ability to push mappings into a waiting queue. if(loader.npmContext) { var context = loader.npmContext; var pkg = context.versions.__default; var conv = context.convert.steal(context, pkg, cfg, true); context.convert.updateConfigOnPackageLoad(conv, false, true, context.applyBuildConfig); oldConfig.apply(loader, arguments); return; } for(var name in cfg) { if(configSpecial[name]) { cfg[name] = configSpecial[name].call(loader, cfg[name]); } } oldConfig.apply(loader, arguments); }; // Implement the newLoader hook to copy config over during the build. var newLoader = System._newLoader || Function.prototype; System._newLoader = function(loader) { loader.npmContext = this.npmContext; loader.npmParentMap = this.npmParentMap; return newLoader.apply(this, arguments); }; steal.addNpmPackages = function(npmPackages) { var packages = npmPackages || []; var loader = this.loader; for (var i = 0; i < packages.length; i += 1) { var pkg = packages[i]; var path = pkg && pkg.fileUrl; if (path) { loader.npmContext.paths[path] = pkg; } } }; steal.getNpmPackages = function() { var context = this.loader.npmContext; return context ? (context.packages || []) : []; } function retryFetch(load, type) { var loader = this; // Get the new moduleName to test against var moduleName = typeof type.name === "function" ? type.name(loader, load) : load.name + type.name; var local = utils.extend({}, load); local.name = moduleName; local.metadata = { dryRun: true }; return Promise.resolve(loader.locate(local)) .then(function(address){ local.address = address; return loader.fetch(local); }) .then(function(source){ load.metadata.address = local.address; loader.npmParentMap[load.name] = local.name; var npmLoad = loader.npmContext && loader.npmContext.npmLoad; if(npmLoad) { npmLoad.saveLoadIfNeeded(loader.npmContext); if(!isNode) { utils.warnOnce("Some 404s were encountered " + "while loading. Don't panic! " + "These will only happen in dev " + "and are harmless."); } } return source; }); } // These define ways to retry a fetch when it fails (404) var retryTypes = [ { name: function(loader, load){ var context = loader.npmContext; if(context.forwardSlashMap[load.name]) { var parts = load.name.split("/"); parts.pop(); return parts.concat(["index"]).join("/"); } return load.name + "/index"; }, test: function() { return true; } }, { name: ".json", test: function(load){ return utils.moduleName.isNpm(load.name) && utils.path.basename(load.address) === "package.js"; } } ]; };