diff --git a/lib/auth.js b/lib/auth.js index fb34f14..9e466d5 100644 --- a/lib/auth.js +++ b/lib/auth.js @@ -1,9 +1,6 @@ -var asp = require('rsvp').denodeify; +var asp = require('bluebird').promisify; var request = require('request'); -if (typeof Promise === 'undefined') { - // To make tests work while running under node 0.10 - Promise = require('rsvp').Promise; -} +var Promise = require('bluebird'); // avoid storing passwords as plain text in config function encodeCredentials(auth) { @@ -21,9 +18,9 @@ function decodeCredentials(str) { exports.decodeCredentials = decodeCredentials; // given options for request, add the auth header / option as appropriate -function injectRequestOptions(requestOptions, auth) { - if (!auth) - return requestOptions; +function injectRequestOptions(requestOptions, registryInfo) { + registryInfo = registryInfo || {}; + var auth = registryInfo.auth || {}; if (auth.username) requestOptions.auth = { user: auth.username, @@ -33,6 +30,10 @@ function injectRequestOptions(requestOptions, auth) { requestOptions.headers = requestOptions.headers || {}; requestOptions.headers.authorization = 'Bearer ' + auth.token; } + if (registryInfo.ca) + requestOptions.agentOptions = { + ca: registryInfo.ca + }; return requestOptions; } exports.injectRequestOptions = injectRequestOptions; @@ -87,7 +88,7 @@ function configureCredentials(registry, _auth, ui) { .then(function() { return asp(request)(injectRequestOptions({ uri: registry - }, _auth)); + }, {auth: _auth})); }) .then(function(res) { if (res.statusCode == 401) diff --git a/lib/node-conversion.js b/lib/node-conversion.js index 7a7038c..6078cfe 100644 --- a/lib/node-conversion.js +++ b/lib/node-conversion.js @@ -1,542 +1,549 @@ -var Promise = require('rsvp').Promise; -var asp = require('rsvp').denodeify; -var nodeSemver = require('semver'); +var Promise = require('bluebird'); +var asp = require('bluebird').promisify; var fs = require('graceful-fs'); var path = require('path'); -var glob = require('glob'); -var npmResolve = require('resolve'); - -var nodeBuiltins = { - 'assert': 'github:jspm/nodelibs-assert@^0.1.0', - 'buffer': 'github:jspm/nodelibs-buffer@^0.1.0', - 'child_process': 'github:jspm/nodelibs-child_process@^0.1.0', - 'cluster': 'github:jspm/nodelibs-cluster@^0.1.0', - 'console': 'github:jspm/nodelibs-console@^0.1.0', - 'constants': 'github:jspm/nodelibs-constants@^0.1.0', - 'crypto': 'github:jspm/nodelibs-crypto@^0.1.0', - 'dgram': 'github:jspm/nodelibs-dgram@^0.1.0', - 'dns': 'github:jspm/nodelibs-dns@^0.1.0', - 'domain': 'github:jspm/nodelibs-domain@^0.1.0', - 'events': 'github:jspm/nodelibs-events@^0.1.1', - 'fs': 'github:jspm/nodelibs-fs@^0.1.0', - 'http': 'github:jspm/nodelibs-http@^1.7.0', - 'https': 'github:jspm/nodelibs-https@^0.1.0', - 'module': 'github:jspm/nodelibs-module@^0.1.0', - 'net': 'github:jspm/nodelibs-net@^0.1.0', - 'os': 'github:jspm/nodelibs-os@^0.1.0', - 'path': 'github:jspm/nodelibs-path@^0.1.0', - 'process': 'github:jspm/nodelibs-process@^0.1.0', - 'punycode': 'github:jspm/nodelibs-punycode@^0.1.0', - 'querystring': 'github:jspm/nodelibs-querystring@^0.1.0', - 'readline': 'github:jspm/nodelibs-readline@^0.1.0', - 'repl': 'github:jspm/nodelibs-repl@^0.1.0', - 'stream': 'github:jspm/nodelibs-stream@^0.1.0', - 'string_decoder': 'github:jspm/nodelibs-string_decoder@^0.1.0', - 'sys': 'github:jspm/nodelibs-util@^0.1.0', - 'timers': 'github:jspm/nodelibs-timers@^0.1.0', - 'tls': 'github:jspm/nodelibs-tls@^0.1.0', - 'tty': 'github:jspm/nodelibs-tty@^0.1.0', - 'url': 'github:jspm/nodelibs-url@^0.1.0', - 'util': 'github:jspm/nodelibs-util@^0.1.0', - 'vm': 'github:jspm/nodelibs-vm@^0.1.0', - 'zlib': 'github:jspm/nodelibs-zlib@^0.1.0' -}; - -var jsonPlugin = exports.jsonPlugin = 'github:systemjs/plugin-json@^0.1.0'; - -exports.convertPackage = function(pjson, dir) { - var packageName = pjson.name; - - // prepare any aliases we need to create - var aliases = {}; - - if (typeof pjson.browser == 'object') { - var curAlias; - var curTarget; - for (var module in pjson.browser) { - curAlias = module; - curTarget = pjson.browser[module]; - - if (typeof curTarget != 'string') - continue; - - // only looking at local aliases here - if (curAlias.substr(0, 2) != './') - continue; - - if (curAlias.substr(0, 2) == './') - curAlias = curAlias.substr(2); - if (curAlias.substr(curAlias.length - 3, 3) == '.js') - curAlias = curAlias.substr(0, curAlias.length - 3); +var url = require('url'); +var readdirp = require('readdirp'); +var detectFormat = require('./node-transformer').detectFormat; +var parseCJS = require('./node-transformer').parseCJS; + +var nodelibsPathPrefix = 'npm:jspm-nodelibs-'; +var nodelibsVersion = '@^0.2.0'; + +var nodeCoreModules = [ + 'assert', + 'buffer', + 'child_process', + 'cluster', + 'console', + 'constants', + 'crypto', + 'dgram', + 'dns', + 'domain', + 'events', + 'fs', + 'http', + 'https', + 'module', + 'net', + 'os', + 'path', + 'process', + 'punycode', + 'querystring', + 'readline', + 'repl', + 'stream', + 'string_decoder', + 'sys', + 'timers', + 'tls', + 'tty', + 'url', + 'util', + 'vm', + 'zlib' +]; + +exports.convertPackage = function(packageConfig, packageName, packageDir, ui) { + packageName = packageName.split(':')[1]; + packageName = packageName.substr(0, packageName.lastIndexOf('@')); + + var systemConfig = packageConfig.systemjs || packageConfig; + + // glob the file tree of the package + return new Promise(function(resolve, reject) { + var fileTree = {}; + readdirp({ + root: packageDir, + entryType: 'both', + + // not a comprehensive filtering - doesnt handle full minimatching + // but will do simple directory filtering at least + directoryFilter: function(dir) { + var path = dir.path.replace(/\\/g, '/'); + + // ignore .git, node_modules, jspm_packages + if (path.match(/^(\.git|node_modules|jspm_packages)($|\/)/)) { + return false; + } + // files + if (packageConfig.files instanceof Array) { + if (!packageConfig.files.some(function(file) { + file = file.replace(/(\/\*\*\/\*|\/|\/\*|\/\*\*)$/, ''); + return path.substr(0, file.length) == file && (path.length == file.length || path[file.length] == '/') || + file.substr(0, path.length) == path && file[path.length] == '/'; + })) + return false; + } + // ignores + if (packageConfig.ignore instanceof Array) { + if (packageConfig.ignore.some(function(ignore) { + return path.substr(0, ignore.length) == ignore && (path.length == ignore.length || path[ignore.length] == '/'); + })) + return false; + } + return true; + } + }, function(entry) { + var listingName = entry.path.replace(/\\/g, '/'); + if (entry.stat.isDirectory()) + listingName += '/'; + fileTree[listingName] = true; + }, function(err) { + if (err) + reject(err); + else + resolve(fileTree); + }); + }) + .then(function(fileTree) { + if (systemConfig.main) { + systemConfig.main = stripPathExtremes(systemConfig.main); + } + systemConfig.main = nodeResolve('./' + systemConfig.main, '', fileTree) || nodeResolve('./index', '', fileTree); - if (curTarget.substr(curTarget.length - 3, 3) == '.js') - curTarget = curTarget.substr(0, curTarget.length - 3); + var coreDeps = []; - aliases[curAlias] = curTarget; + // meta and map are independently populated from the tree only if not already existing + var meta; + if (!systemConfig.meta) { + meta = { + '*.json': { + format: 'json' + } + }; } - } - - var buildErrors = []; - var newDeps = {}; - return asp(glob)(dir + path.sep + '**' + path.sep + '*.js') - .then(function(files) { + var map; + if (!systemConfig.map) + map = {}; + + // convert package.json browser maps if map doesn't yet exist + if (map) { + // browser / browserify main + if (!map['./' + systemConfig.main]) { + var browserMain; + if (typeof packageConfig.browser == 'string') + browserMain = packageConfig.browser; + else if (typeof packageConfig.browserify == 'string') + browserMain = packageConfig.browserify; + + if (browserMain) { + browserMain = stripPathExtremes(browserMain); + browserMain = nodeResolve('./' + browserMain, '', fileTree); + if (browserMain && browserMain != systemConfig.main) + map['./' + systemConfig.main] = { + browser: './' + browserMain + }; + } + } - // we store the list of directory files to make - // only writing after this step to avoid incorrect internal resolutions - var directoryFiles = []; + if (typeof packageConfig.browser == 'object') + for (var b in packageConfig.browser) { + // dont replace any existing map config + if (map[b]) + continue; + + var mapping = packageConfig.browser[b]; + var mapResolve; + + if (mapping === false) + mapResolve = '@empty'; + else if (typeof mapping == 'string') + mapResolve = nodeResolve(mapping, '', fileTree, true); + + if (mapResolve) { + var browserMap = map[b] = { + browser: mapResolve + }; + + // if the map is a dependency, then make sure we have a fallback path to that dependency + // for the non-browser case + var fallbackName = 'node-' + b; + var createdFallback = Object.keys(packageConfig.dependencies).some(function(dep) { + if (b.substr(0, dep.length) == dep && (b.length == dep.length || b[dep.length] == '/')) { + var depObj = systemConfig.dependencies = systemConfig.dependencies || {}; + depObj[fallbackName] = packageConfig.dependencies[dep]; + return true; + } + }); + if (!createdFallback) + Object.keys(packageConfig.peerDependencies).some(function(dep) { + if (b.substr(0, dep.length) == dep && (b.length == dep.length || b[dep.length] == '/')) { + var depObj = systemConfig.peerDependencies = systemConfig.peerDependencies || {}; + depObj[fallbackName] = packageConfig.peerDependencies[dep]; + return true; + } + }); + + if (createdFallback) + browserMap.default = fallbackName; + } + } + } - return Promise.all(files.map(function(file) { - var filename = path.relative(dir, file).replace(/\\/g, '/'); + // modules that need to be added as meta to skip extension handling + var skipExtensions = []; + + // track paths that have all common meta + // to simplify with wildcards + var parsedCommonMeta = {}; + + // run through each file in the tree + return Promise.all(Object.keys(fileTree).filter(function(file) { + return file[file.length - 1] != '/' + && file.substr(file.length - 3, 3) != '.md' + && file != '.npmignore' + && file != '.gitignore'; + }).map(function(fileName) { + var filePath = path.resolve(packageDir, fileName).replace(/\\/g, '/'); + + // when generating meta, set up the empty object now + var fileFormat = systemConfig.format; + if (meta) { + var curMeta = {}; + if (filePath.substr(filePath.length - 5) == '.json') + fileFormat = 'json'; + } - // skip files in the ignore paths - // NB this can be removed with https://github.com/jspm/jspm-cli/issues/345 - if (pjson.ignore) { - if (pjson.ignore.some(function(path) { - return filename.substr(0, path.length) == path && (filename.substr(path.length, 1) == '/' || filename.substr(path.length, 1) === ''); - })) - return; + // otherwise read format comprehensively from existing metadata + else { + fileFormat = readMeta(fileName, systemConfig.meta).format || systemConfig.format; } - filename = filename.substr(0, filename.length - 3); - var source; - var changed = false; - return Promise.resolve() + var fileSource; + // if not known, apply detection from source file + return Promise.resolve() .then(function() { - // if its an "index.js" file, then check if we can create a directory shortcut for it - var parts = filename.split('/'); - if (parts.pop() != 'index') + if (fileFormat) return; - var dirName = parts.join(path.sep); - var dirModule = path.resolve(dir, dirName) + '.js'; - return new Promise(function(resolve, reject) { - fs.exists(dirModule, resolve); - }) - .then(function(exists) { - if (exists) - return; - directoryFiles.push(dirModule); - }); - }) + return asp(fs.readFile)(filePath) + .then(function(source) { + fileSource = source.toString(); - .then(function() { - return asp(fs.readFile)(file); + return detectFormat(fileSource); + }); }) + .then(function(detectedFormat) { + if (detectedFormat == 'es6') + detectedFormat = 'esm' + + if (detectedFormat) + fileFormat = detectedFormat; + + // provide all module format detections as configuration if not the package format + if (meta && detectedFormat && detectedFormat != (systemConfig.format || 'cjs')) + curMeta.format = detectedFormat; + + // non-cjs -> disable process and we're done + if (fileFormat != 'cjs') { + // global is only format to also support globals + if (meta && fileFormat == 'global') { + curMeta.globals = curMeta.globals || {}; + curMeta.globals['process'] = null; + } + return; + } - .then(function(_source) { - source = _source.toString(); - - // at this point, only alter the source file if we're certain it is CommonJS in Node-style - - // first check if we have format meta - var meta = source.match(metaRegEx); - var metadata = {}; - if (meta) { - var metaParts = meta[0].match(metaPartRegEx); - for (var i = 0; i < metaParts.length; i++) { - var len = metaParts[i].length; - - var firstChar = metaParts[i].substr(0, 1); - if (metaParts[i].substr(len - 1, 1) == ';') - len--; - - if (firstChar != '"' && firstChar != "'") - continue; + // cjs -> parse the source for requires and see if it uses Buffer + return Promise.resolve(fileSource || asp(fs.readFile)(filePath)) + .then(function(source) { + var parsed = parseCJS(source.toString(), !meta || !meta['*'] || !meta['*'].globals || !meta['*'].globals.process); - var metaString = metaParts[i].substr(1, metaParts[i].length - 3); + if (meta && parsed.requireDetect === false) { + if (parsed.requires.length) + curMeta.deps = parsed.requires; + curMeta.cjsRequireDetection = false; + } - var metaName = metaString.substr(0, metaString.indexOf(' ')); - if (metaName) { - var metaValue = metaString.substr(metaName.length + 1, metaString.length - metaName.length - 1); + // add buffer global for CJS files that need it + if (meta && parsed.usesBuffer && (!systemConfig.map || systemConfig.map.buffer != '@empty')) { + curMeta.globals = curMeta.globals || {}; + curMeta.globals.Buffer = 'buffer/global'; + if (coreDeps.indexOf('buffer') == -1) + coreDeps.push('buffer'); + } - if (metadata[metaName] instanceof Array) - metadata[metaName].push(metaValue); - else - metadata[metaName] = metaValue; - } + if (meta && parsed.usesProcess && (!systemConfig.map || systemConfig.map.process != '@empty')) { + coreDeps.push('process'); + meta['*'] = meta['*'] || {}; + meta['*'].globals = meta['*'].globals || {}; + meta['*'].globals.process = 'process'; } - } - if (pjson.format != 'cjs' && !metadata.format) - return; + /* + * Comprehensive internal resolution differences between SystemJS and Node + * for internal package requires (that is ignoring package.json, node_modules) + * + * 1. Directory requires won't resolve to index.js in the directory + * 2. Files that resolve to .json, not already ending in .json, are mapped + * 3. Files that don't end in .js, that are actual files, will not add extensions + * 4. A file by the name file.js.js loaded as file.js will not add extensions + * 5. A file by the name file.json.js loaded as file.json will not add extensions + * + * Currently we cater to (1) by creating a directory map for any index.js file present + * in a directory where the directory.js file does not exist. + * We then cater to (2 - 5) above by parsing all CommonJS requires of all JS files in + * the package and where a resolution matches one of these cases, we include map or meta + * config for these files. + * + * We cater to these assumptions for CommonJS modules only + * + * It may turn out to be useful to do (2 - 5) for external requires as well, in which + * case we can switch this algorithm to a comprehensive correction configuration + * being constructed against the entire fileTree to handle all resolution differences. + * Even better may turn out to have a post-install hook phase, which can actually investigate + * the contents of dependencies to do a similar analysis above to avoid config bloat + */ + // 1. directory resolution + if (map && fileName.substr(fileName.length - 9, 9) == '/index.js' && !fileTree[fileName.substr(0, fileName.length - 9)]) + map['./' + fileName.substr(0, fileName.length - 9)] = './' + fileName; + + parsed.requires.forEach(function(req) { + // package require by own name + if (req.substr(0, packageName.length) == packageName && (req[packageName.length] == '/' || req.length == packageName.length)) { + if (map) + map[packageName] = '.'; + return; + } - if (metadata.format && metadata.format != 'cjs') - return; + // if it is a package require, note if we have a new core dep + if (req[0] != '.') { + // sys is an alias for util in Node + if (req == 'sys') + req = 'util'; + if (nodeCoreModules.indexOf(req) != -1 && coreDeps.indexOf(req) == -1) + coreDeps.push(req); + return; + } - if (pjson.shim && pjson.shim[filename]) - return; + // resolve ./ relative requires only + var nodeResolved = nodeResolve(req, fileName, fileTree); - if (source.match(cmdCommentRegEx)) - source = '//' + source; + // if it didn't resolve, ignore it + if (!nodeResolved || nodeResolved == '.') + return; - // Note an alternative here would be to use https://github.com/substack/insert-module-globals - var usesBuffer = source.match(bufferRegEx), usesProcess = source.match(processRegEx); + // 2. auto json extension adding + if (map && nodeResolved.substr(nodeResolved.length - 5, 5) == '.json' && req.substr(req.length - 5, 5) != '.json') + map['./' + nodeResolved.substr(0, nodeResolved.length - 5)] = './' + nodeResolved; - // the buffer and process nodelibs modules themselves don't wrap themselves - if (pjson.name == 'buffer') - usesBuffer = false; - if (pjson.name == 'process') - usesProcess = false; + // 3. non js file extension + else if (meta && nodeResolved.substr(nodeResolved.length - 3, 3) != '.js' && nodeResolved != '.' && nodeResolved != '..') + skipExtensions.push(nodeResolved); - if (usesBuffer || usesProcess) { - changed = true; - source = "(function(" + (usesBuffer && 'Buffer' || '') + (usesBuffer && usesProcess && ", " || '') + (usesProcess && 'process' || '') + ") {" + source + - "\n})(" + (usesBuffer && "require('buffer').Buffer" || '') + (usesBuffer && usesProcess && ", " || '') + (usesProcess && "require('process')" || '') + ");"; + // 4. file.js.js + // 5. file.json.js + else if (map && (nodeResolved.substr(nodeResolved.length - 6, 6) == '.js.js' || nodeResolved.substr(nodeResolved.length - 8, 8) == '.json.js')) + map['./' + nodeResolved.substr(0, nodeResolved.length - 3)] = './' + nodeResolved; + }); + }); + }) + .then(function() { + // set curMeta on the main meta object + if (meta) { + meta[fileName] = curMeta; + + // note common meta for wildcard simplification + var pathParts = fileName.split('/'); + for (var i = 1; i < pathParts.length; i++) { + var curCommonPath = pathParts.slice(0, i).join('/'); + var curCommonParsed = parsedCommonMeta[curCommonPath] = parsedCommonMeta[curCommonPath] || extend({}, curMeta) || {}; + if (curCommonParsed.format && curCommonParsed.format != fileFormat) + curCommonParsed.format = undefined; + if (curCommonParsed.globals && JSON.stringify(curCommonParsed.globals) != JSON.stringify(curMeta.globals)) + curCommonParsed.globals = undefined; + } } + }); + })) + .then(function() { - // remap require statements, with mappings: - // require('file.json') -> require('file.json!') - // finally we map builtins to the adjusted module - var baseDir = path.dirname(path.relative(dir, file)); - - // only remap if commonjs - return require('systemjs-builder/compilers/cjs').remap(source, function(dep) { - var relPath = path.join(baseDir, dep.replace(/\//g, path.sep)).replace(/\\/g, '/'); - var firstPart = dep.split('/').splice(0, dep.substr(0, 1) == '@' ? 2 : 1).join('/'); - - // packages can import themselves by name - if (firstPart == packageName) { - // note mains work here because the npmResolve below picks up the jspm-generated main file - var depPath = path.join(dir, dep.replace(firstPart, '').replace(/\//g, path.sep)); - dep = path.relative(path.dirname(file), depPath).replace(/\\/g, '/'); - if (dep.substr(0, 1) != '.') - dep = './' + dep; - } + systemConfig.format = systemConfig.format || 'cjs'; - // first check if this is an alias - if (aliases[relPath]) { - changed = true; - dep = path.relative(baseDir, aliases[relPath].replace(/\//g, path.sep)).replace(/\\/g, '/'); - if (dep.substr(0, 1) != '.') - dep = './' + dep; - } + // add meta alphabetically + if (hasProperties(meta)) { + systemConfig.meta = {}; - // check if it is a Node builtin - else if (!pjson.dependencies[firstPart] && nodeBuiltins[firstPart]) { - changed = true; - // only add explicit builtin dependency for production code - if (!filename.match(/^(test|tests|support|example)\//)) - newDeps[firstPart] = nodeBuiltins[firstPart]; - return firstPart; - } + // consolidate common meta + Object.keys(parsedCommonMeta).reverse().forEach(function(commonPath) { + var curMeta = parsedCommonMeta[commonPath]; + if (!curMeta.format && !curMeta.globals) + return; - // if not a package, check for internal resolution - // run the NodeJS resolver, to know which file we should get - else if (!pjson.dependencies[firstPart]) { - try { - var resolved = npmResolve.sync(dep, { basedir: path.dirname(file), extensions: ['.js', '.json'] }); - - // too deep - not a jspm resolution - if (resolved.substr(0, dir.length) == dir) { - dep = path.relative(path.dirname(file), resolved).replace(/\\/g, '/'); - if (dep.substr(0, 1) != '.') - dep = './' + dep; - - relPath = path.relative(dir, resolved); - if (relPath.substr(relPath.length - 3, 3) == '.js') - relPath = relPath.substr(0, relPath.length - 3); - } + Object.keys(meta).forEach(function(path) { + if (path.substr(0, commonPath.length) == commonPath && path[commonPath.length] == '/') { + if (curMeta.format && meta[path].format == curMeta.format) + delete meta[path].format; + if (curMeta.globals && JSON.stringify(meta[path].globals) == JSON.stringify(curMeta.globals)) + delete meta[path].globals; } - catch(e) {} - } - - // check if it is an alias again - if (aliases[relPath]) { - changed = true; - dep = path.relative(baseDir, aliases[relPath].replace(/\//g, path.sep)).replace(/\\/g, '/'); - if (dep.substr(0, 1) != '.') - dep = './' + dep; - } - - // now that we have resolved the dependency, do extension alterations - if (dep.substr(dep.length - 5, 5) == '.json') { - changed = true; - newDeps['systemjs-json'] = jsonPlugin; - return dep + '!systemjs-json'; - } + }); + meta[commonPath + '/*'] = curMeta; + }); - // disable directory requires - if (dep.substr(dep.length - 1, 1) == '/') { - changed = true; - dep = dep.substr(0, dep.length - 1); - } + // ensure all skip extensions either have exact meta or extension meta or true + skipExtensions.forEach(function(m) { + if (!(hasProperties(meta[m]) || hasProperties(meta['*.' + m.split('/').pop().split('.').pop()]))) + meta[m] = true; + }); - // remove js extensions - if (dep.substr(dep.length - 3, 3) == '.js' && dep.indexOf('/') != -1) { - changed = true; - return dep.substr(0, dep.length - 3); - } + Object.keys(meta).sort().forEach(function(m) { + // clear out unnecessary empty metas + // only set metas that aren't true or empty + if (meta[m] === true || hasProperties(meta[m])) + systemConfig.meta[m] = meta[m]; + }); + } - return dep; - }, file) - .then(function(output) { - source = output.source; + // add map alphabetically + if (map && hasProperties(map)) { + systemConfig.map = {}; + Object.keys(map).sort().forEach(function(m) { + systemConfig.map[m] = map[m]; }); - }) - .then(function(output) { - Object.keys(newDeps).forEach(function(dep) { - pjson.dependencies[dep] = newDeps[dep]; + } + + // add core dependencies + function inMapTargets(dep) { + return Object.keys(systemConfig.map).some(function(map) { + var target = systemConfig.map[map]; + if (typeof target == 'string') + target = { '1': target }; + return Object.keys(target).some(function(submap) { + var curTarget = target[submap]; + return curTarget.substr(0, dep.length) == dep && (curTarget[dep.length] == '/' || curTarget.length == dep.length); + }); }); + } - if (!changed) + systemConfig.peerDependencies = packageConfig.peerDependencies || {}; + coreDeps.sort().forEach(function(dep) { + if (systemConfig.map && systemConfig.map[dep] && typeof systemConfig.map[dep] == 'string' && !inMapTargets(dep) || + packageConfig.peerDependencies && packageConfig.peerDependencies[dep] || packageConfig.dependencies && packageConfig.dependencies[dep]) return; - - return asp(fs.writeFile)(file, source); - }, function(err) { - buildErrors.push(err); + if (packageConfig.browserifyCore !== false) + systemConfig.peerDependencies[dep] = nodelibsPathPrefix + dep + nodelibsVersion; + else + systemConfig.map[dep] = '@node/' + dep; }); - })) - .then(function() { - // write directory forwarding files for external directory requires - return Promise.all(directoryFiles.map(function(dirFile) { - var dirName = dirFile.split(path.sep).pop(); - dirName = dirName.substr(0, dirName.length - 3).replace(/\\/g, '/'); - return fs.writeFile(dirFile, "module.exports = require('./" + dirName + "/index');\n"); - })); + + return systemConfig; }); - }) - .then(function() { - return buildErrors; }); -} +}; -var bufferRegEx = /(?:^|[^$_a-zA-Z\xA0-\uFFFF.])Buffer/; -var processRegEx = /(?:^|[^$_a-zA-Z\xA0-\uFFFF.])process/; - -var metaRegEx = /^(\s*\/\*.*\*\/|\s*\/\/[^\n]*|\s*"[^"]+"\s*;?|\s*'[^']+'\s*;?)+/; -var metaPartRegEx = /\/\*.*\*\/|\/\/[^\n]*|"[^"]+"\s*;?|'[^']+'\s*;?/g; - -var cmdCommentRegEx = /^\s*#/; - -// convert NodeJS or Bower dependencies into jspm-compatible dependencies -var githubRegEx = /^(git(\+[^:]+)?|https):\/\/github.com\/(.+)/; -var githubHttpRegEx = /^https?:\/\/github\.com\/([^\/]+\/[^\/]+)\/archive\/([^\/]+)\.(tar\.gz|zip)$/; -var protocolRegEx = /^[^\:\/]+:\/\//; -var semverRegEx = /^(\d+)(?:\.(\d+)(?:\.(\d+)(?:-([\da-z-]+(?:\.[\da-z-]+)*)(?:\+([\da-z-]+(?:\.[\da-z-]+)*))?)?)?)?$/i; -function parseDependencies(dependencies, ui) { - // do dependency parsing - var outDependencies = {}; - var process = function(d) { - var dep = dependencies[d]; - - var match, name, version = ''; - - // 1. git://github.com/name/repo.git#version -> github:name/repo@version - if ((match = dep.match(githubRegEx))) { - dep = match[3]; - name = 'github:' + dep.split('#')[0]; - version = dep.split('#')[1] || '*'; - if (version.substr(0, 1) == 'v' && version.substr(1).match(semverRegEx)) - version = version.substr(1); - if (name.substr(name.length - 4, 4) == '.git') - name = name.substr(0, name.length - 4); - ui.log('warn', 'npm dependency `' + name + '@' + version + '` will likely only work if its GitHub repo has %registry: npm% in its package.json'); +// pulled out of SystemJS internals... +function readMeta(pkgPath, pkgMeta) { + var meta = {}; + + // apply wildcard metas + var bestDepth = 0; + var wildcardIndex; + for (var module in pkgMeta) { + wildcardIndex = module.indexOf('*'); + if (wildcardIndex === -1) + continue; + if (module.substr(0, wildcardIndex) === pkgPath.substr(0, wildcardIndex) + && module.substr(wildcardIndex + 1) === pkgPath.substr(pkgPath.length - module.length + wildcardIndex + 1)) { + var depth = module.split('/').length; + if (depth > bestDepth) + bestDepth = depth; + extendMeta(meta, pkgMeta[module], bestDepth != depth); } + } - // 2. https?://github.com/name/repo/archive/v?[semver].tar.gz -> github:name/repo@[semver] - else if ((match = dep.match(githubHttpRegEx))) { - name = 'github:' + match[1]; - version = match[2]; - if (version.substr(0, 1) == 'v' && version.substr(1).match(semverRegEx)) - version = version.substr(1); - } + // apply exact meta + if (meta[pkgPath]) + extendMeta(load.metadata, meta[pkgPath]); - // 3. url:// -> not supported - else if (dep.match(protocolRegEx)) - throw 'npm dependency format ' + dep + ' not currently supported by jspm. Post an issue if required.'; + return meta; +} +function extendMeta(a, b, prepend) { + for (var p in b) { + var val = b[p]; + if (!(p in a)) + a[p] = val; + else if (val instanceof Array && a[p] instanceof Array) + a[p] = [].concat(prepend ? val : a[p]).concat(prepend ? a[p] : val); + else if (typeof val == 'object' && typeof a[p] == 'object') + a[p] = extend(extend({}, a[p]), val, prepend); + else if (!prepend) + a[p] = val; + } +} +function extend(a, b, prepend) { + for (var p in b) { + if (!prepend || !(p in a)) + a[p] = b[p]; + } + return a; +} - // 4. name/repo#version -> github:name/repo@version - else if (dep.split('/').length == 2) { - name = 'github:' + dep.split('#')[0]; - version = dep.split('#')[1] || '*'; - if (version.substr(0, 1) == 'v' && version.substr(1).match(semverRegEx)) - version = version.substr(1); - } +function hasProperties(obj) { + for (var p in obj) + return true; + return false; +} - // 5. version -> name@version - else { - name = d; - version = dep; - } +/* + * Given a file tree stat, work out the resolution for a package + * name is a path within the package, parent is also a path within the package + * fileTree is keyed by path, with true as the value. Folders are indicated by trailling / + * All paths are assumed '/' separated for this implementation + */ +function nodeResolve(name, parent, fileTree, dotRel) { + var dotPrefix = dotRel ? './' : ''; - // otherwise, we convert an npm range into something jspm-compatible - // if it is an exact semver, or a tag, just use it directly - if (!nodeSemver.valid(version)) { - var range; - - // comma space is allowed on npm for some reason - version = version.replace(/, /g, ' '); - - if (!version || version == 'latest' || version == '*') - version = '*'; - - // if we have a semver or fuzzy range, just keep as-is - else if (version.indexOf(/[ <>=]/) != -1 || !version.substr(1).match(semverRegEx) || !version.substr(0, 1).match(/[\^\~]/)) - range = nodeSemver.validRange(version); - - if (range == '*') - version = '*'; - - else if (range) { - // if it has OR semantics, we only support the last range - if (range.indexOf('||') != -1) - range = range.split('||').pop(); - - var rangeParts = range.split(' '); - - // convert AND statements into a single lower bound and upper bound - // enforcing the lower bound as inclusive and the upper bound as exclusive - var lowerBound, upperBound, lEq, uEq; - for (var i = 0; i < rangeParts.length; i++) { - var part = rangeParts[i]; - var a = part.charAt(0); - var b = part.charAt(1); - - // get the version - var v = part; - if (b == '=') - v = part.substr(2); - else if (a == '>' || a == '<' || a == '=') - v = part.substr(1); - - // and the operator - var gt = a == '>'; - var lt = a == '<'; - - if (gt) { - // take the highest lower bound - if (!lowerBound || nodeSemver.gt(lowerBound, v)) { - lowerBound = v; - lEq = b == '='; - } - } - else if (lt) { - // take the lowest upper bound - if (!upperBound || nodeSemver.lt(upperBound, v)) { - upperBound = v; - uEq = b == '='; - } - } - else { - // equality - lowerBound = upperBound = (part.substr(0, 1) == '=' ? part.substr(1) : part); - lEq = uEq = true; - break; - } - } + // leave absolute paths undisturbed + if (name[0] == '/') + return; - // for some reason nodeSemver adds "-0" when not appropriate - if (lowerBound && lowerBound.substr(lowerBound.length - 2, 2) == '-0') - lowerBound = lowerBound.substr(0, lowerBound.length - 2); - if (upperBound && upperBound.substr(upperBound.length - 2, 2) == '-0') - upperBound = upperBound.substr(0, upperBound.length - 2); - - var lowerSemver, upperSemver; - - if (lowerBound) { - lowerSemver = lowerBound.match(semverRegEx); - lowerSemver[1] = parseInt(lowerSemver[1], 10); - lowerSemver[2] = parseInt(lowerSemver[2], 10); - lowerSemver[3] = parseInt(lowerSemver[3], 10); - if (!lEq) { - if (!lowerSemver[4]) - lowerSemver[4] = '0'; - // NB support incrementing existing preleases - } - } + // relative paths are resolved relatively and statted + if (name.substr(0, 2) == './' || name.substr(0, 3) == '../' && parent.indexOf('/') != -1) { + name = url.resolve('/' + parent, name).substr(1); - if (upperBound) { - upperSemver = upperBound.match(semverRegEx); - upperSemver[1] = parseInt(upperSemver[1], 10); - upperSemver[2] = parseInt(upperSemver[2], 10); - upperSemver[3] = parseInt(upperSemver[3], 10); - } + if (!name) + name = '.'; - if (!upperBound && !lowerBound) { - version = ''; - } + if (name[name.length - 1] != '/') { + if (fileTree.hasOwnProperty(name)) + return dotPrefix + name; - // if not upperBound, then just treat as a wildcard - else if (!upperBound) { - version = '*'; - } + if (fileTree.hasOwnProperty(name + '.js')) + return dotPrefix + name + '.js'; - // if no lowerBound, use the upperBound directly, with sensible decrementing if necessary - else if (!lowerBound) { + if (fileTree.hasOwnProperty(name + '.json')) + return dotPrefix + name + '.json'; + } - if (uEq) { - version = upperBound; - } + // no file match -> try loading as a folder + var folderName = name + (name[name.length - 1] == '/' ? '' : '/'); - else { - if (!upperSemver[4]) { - if (upperSemver[3] > 0) { - upperSemver[3]--; - } - else if (upperSemver[2] > 0) { - upperSemver[2]--; - upperSemver[3] = 0; - } - else if (upperSemver[1] > 0) { - upperSemver[1]--; - upperSemver[2] = 0; - upperSemver[3] = 0; - } - } - else { - upperSemver[4] = undefined; - } - version = getVersion(upperSemver); - } - } + if (fileTree.hasOwnProperty(folderName)) + return dotPrefix + folderName + 'index.js'; - else { - // if upper bound is inclusive, use it - if (uEq) - version = upperBound; + // unable to resolve -> ignore + return; + } - // if upper bound is exact major - else if (upperSemver[2] === 0 && upperSemver[3] === 0 && !upperSemver[4]) { + // plain name -> package resolution + return name; +} - // if previous major is 0 - if (upperSemver[1] - 1 === 0) { - version = '0'; - } - else { - // if lower bound is major below, we are semver compatible - if (lowerSemver[1] == upperSemver[1] - 1) - version = '^' + getVersion(lowerSemver); - // otherwise we are semver compatible with the previous exact major - else - version = '^' + (upperSemver[1] - 1); - } - } - // if upper bound is exact minor - else if (upperSemver[3] === 0 && !upperSemver[4]) { - // if lower bound is minor below, we are fuzzy compatible - if (lowerSemver[2] == upperSemver[2] - 1) - version = '~' + getVersion(lowerSemver); - // otherwise we are fuzzy compatible with previous - else - version = '~' + upperSemver[1] + '.' + (upperSemver[2] - 1) + '.0'; - } - // if upper bound is exact version -> use exact - else - throw 'Unable to translate npm version ' + version + ' into a jspm range.'; - } - } - } +// Removes './' from the beginning of paths or '/' from the end of paths, etc +function stripPathExtremes(path) { + if (path == '.') + path = ''; + else if (path.substr(0, 2) == './') + path = path.substr(2); - outDependencies[d] = name + (version ? '@' + version : ''); - }; - for (var d in dependencies) process(d); - return outDependencies; -} + if (path[path.length - 1] == '/') + path = path.substr(0, path.length - 1); -function getVersion(semver) { - return semver[1] + '.' + semver[2] + '.' + semver[3] + (semver[4] ? '-' + semver[4] : ''); + return path; } -exports.parseDependencies = parseDependencies; - diff --git a/lib/node-transformer.js b/lib/node-transformer.js new file mode 100644 index 0000000..7538c69 --- /dev/null +++ b/lib/node-transformer.js @@ -0,0 +1,238 @@ +var traceur = require('traceur'); +var traceurVersion = traceur.loader.NodeTraceurLoader.prototype.version; + +function traceurGet (module) { + return $traceurRuntime.ModuleStore.get('traceur@' + traceurVersion + '/src/' + module); +} + +var ParseTreeTransformer = traceurGet('codegeneration/ParseTreeTransformer.js').ParseTreeTransformer; +var ScopeTransformer = traceurGet('codegeneration/ScopeTransformer.js').ScopeTransformer; + + +var Script = traceurGet('syntax/trees/ParseTrees.js').Script; +var parseStatements = traceurGet('codegeneration/PlaceholderParser.js').parseStatements; +var STRING = traceurGet('syntax/TokenType.js').STRING; +var LiteralExpression = traceurGet('syntax/trees/ParseTrees.js').LiteralExpression; +var LiteralToken = traceurGet('syntax/LiteralToken.js').LiteralToken; + +// format detection regexes +var leadingCommentAndMetaRegEx = /^\s*(\/\*[^\*]*(\*(?!\/)[^\*]*)*\*\/|\s*\/\/[^\n]*|\s*"[^"]+"\s*;?|\s*'[^']+'\s*;?)*\s*/; +var cjsRequireRegEx = /(?:^\uFEFF?|[^$_a-zA-Z\xA0-\uFFFF."'])require\s*\(\s*("[^"\\]*(?:\\.[^"\\]*)*"|'[^'\\]*(?:\\.[^'\\]*)*')\s*\)/g; +var cjsExportsRegEx = /(?:^\uFEFF?|[^$_a-zA-Z\xA0-\uFFFF.])(exports\s*(\[['"]|\.)|module(\.exports|\['exports'\]|\["exports"\])\s*(\[['"]|[=,\.]))/; +var amdRegEx = /(?:^\uFEFF?|[^$_a-zA-Z\xA0-\uFFFF.])define\s*\(\s*("[^"]+"\s*,\s*|'[^']+'\s*,\s*)?\s*(\[(\s*(("[^"]+"|'[^']+')\s*,|\/\/.*\r?\n|\/\*(.|\s)*?\*\/))*(\s*("[^"]+"|'[^']+')\s*,?)?(\s*(\/\/.*\r?\n|\/\*(.|\s)*?\*\/))*\s*\]|function\s*|{|[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*\))/; + +var metaRegEx = /^(\s*\/\*[^\*]*(\*(?!\/)[^\*]*)*\*\/|\s*\/\/[^\n]*|\s*"[^"]+"\s*;?|\s*'[^']+'\s*;?)+/; +var metaPartRegEx = /\/\*[^\*]*(\*(?!\/)[^\*]*)*\*\/|\/\/[^\n]*|"[^"]+"\s*;?|'[^']+'\s*;?/g; + +exports.detectFormat = function(source) { + // read any SystemJS meta syntax + var meta = source.match(metaRegEx); + if (meta) { + var metaParts = meta[0].match(metaPartRegEx); + + for (var i = 0; i < metaParts.length; i++) { + var curPart = metaParts[i]; + var len = curPart.length; + + var firstChar = curPart.substr(0, 1); + if (curPart.substr(len - 1, 1) == ';') + len--; + + if (firstChar != '"' && firstChar != "'") + continue; + + var metaString = curPart.substr(1, curPart.length - 3); + + if (metaString.substr(0, 7) == 'format ') + return metaString.substr(7); + else if (metaString == 'bundle') + return 'register'; + } + } + + // detect register + var leadingCommentAndMeta = source.match(leadingCommentAndMetaRegEx); + if (leadingCommentAndMeta && source.substr(leadingCommentAndMeta[0].length, 15) == 'System.register') + return 'register'; + + // esm + try { + var compiler = new traceur.Compiler({ script: false }); + var tree = compiler.parse(source); + var transformer = new ESMDetectionTransformer(); + transformer.transformAny(tree); + if (transformer.isESModule) + return 'esm'; + else + compiler = tree = undefined; + } + catch(e) { + compiler = tree = undefined; + } + + // cjs + // (NB this should be updated to be parser based, with caching by name to parseCJS below) + if (source.match(cjsRequireRegEx) || source.match(cjsExportsRegEx)) + return 'cjs'; + + // amd + if (source.match(amdRegEx)) + return 'amd'; + + // fallback is cjs (not global) + return 'cjs'; +}; + +exports.parseCJS = function(source, detectProcess) { + var output = { + requires: [], + usesBuffer: false, + usesProcess: false, + requireDetect: true + }; + + var compiler, tree, transformer; + + // if this is a browserified module, then we skip require detection entirely + // (webpack and SystemJS builds rename internal hidden require statements so aren't a problem) + if (source.indexOf('(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;') != -1) { + output.requireDetect = false; + return output; + } + + // CJS Buffer detection and require extraction + try { + compiler = new traceur.Compiler({ script: true }); + tree = tree || compiler.parse(source); + } + catch(e) { + return output; + } + + // sets output.requires + transformer = new CJSDepsTransformer(); + try { + transformer.transformAny(tree); + } + catch(e) {} + output.requires = transformer.requires; + + // sets output.usesBuffer + transformer = new GlobalUsageTransformer('Buffer'); + try { + transformer.transformAny(tree); + } + catch(e) {} + output.usesBuffer = !!transformer.usesGlobal; + + // sets output.usesProcess + transformer = new GlobalUsageTransformer('process'); + try { + transformer.transformAny(tree); + } + catch(e) {} + output.usesProcess = !!transformer.usesGlobal; + + return output; +}; + + +function ESMDetectionTransformer() { + this.isESModule = false; + return ParseTreeTransformer.apply(this, arguments); +} +ESMDetectionTransformer.prototype = Object.create(ParseTreeTransformer.prototype); +ESMDetectionTransformer.prototype.transformExportDeclaration = function(tree) { + this.isESModule = true; + return ParseTreeTransformer.prototype.transformExportDeclaration.call(this, tree); +}; +ESMDetectionTransformer.prototype.transformImportDeclaration = function(tree) { + this.isESModule = true; + return ParseTreeTransformer.prototype.transformImportDeclaration.call(this, tree); +}; + +// esm remapping not currently in use +/* +function ESMImportsTransformer() { + this.imports = []; + return ParseTreeTransformer.apply(this, arguments); +} +ESMImportsTransformer.prototype = Object.create(ParseTreeTransformer.prototype); +ESMImportsTransformer.prototype.transformModuleSpecifier = function(tree) { + if (this.imports.indexOf(tree.token.processedValue) == -1) + this.imports.push(tree.token.processedValue); + + return ParseTreeTransformer.prototype.transformModuleSpecifier.call(this, tree); +}; +*/ + +function CJSDepsTransformer() { + this.requires = []; + return ParseTreeTransformer.apply(this, arguments); +} +CJSDepsTransformer.prototype = Object.create(ParseTreeTransformer.prototype); + +CJSDepsTransformer.prototype.transformCallExpression = function(tree) { + if (!tree.operand.identifierToken || tree.operand.identifierToken.value != 'require') + return ParseTreeTransformer.prototype.transformCallExpression.call(this, tree); + + // found a require + var args = tree.args.args; + if (args.length && args[0].type == 'LITERAL_EXPRESSION' && args.length == 1) { + if (this.requires.indexOf(args[0].literalToken.processedValue) == -1 && typeof args[0].literalToken.processedValue == 'string') + this.requires.push(args[0].literalToken.processedValue); + } + + return ParseTreeTransformer.prototype.transformCallExpression.call(this, tree); +}; + +function GlobalUsageTransformer(varName) { + this.usesGlobal = undefined; + return ScopeTransformer.apply(this, arguments); +} +GlobalUsageTransformer.prototype = Object.create(ScopeTransformer.prototype); +GlobalUsageTransformer.prototype.transformIdentifierExpression = function(tree) { + if (tree.identifierToken.value == this.varName_ && this.usesGlobal !== false) + this.usesGlobal = true; + return ScopeTransformer.prototype.transformIdentifierExpression.apply(this, arguments); +}; +GlobalUsageTransformer.prototype.transformBindingIdentifier = function(tree) { + if (tree.identifierToken.value == this.varName_ && this.usesGlobal !== false) + this.usesGlobal = true; + return ScopeTransformer.prototype.transformBindingIdentifier.apply(this, arguments); +}; +GlobalUsageTransformer.prototype.sameTreeIfNameInLoopInitializer_ = function(tree) { + try { + tree = ScopeTransformer.prototype.sameTreeIfNameInLoopInitializer_.call(this, tree); + } + catch(e) {} + return tree; +}; +GlobalUsageTransformer.prototype.transformVariableDeclaration = function(tree) { + if (tree.lvalue.identifierToken.value == this.varName_) + return tree; + return ScopeTransformer.prototype.transformVariableDeclaration.call(this, tree); +}; + +// NB incorrect handling for function Buffer() {}, but we don't have better scope analysis available +// until a shift to Babel :( +GlobalUsageTransformer.prototype.transformFunctionDeclaration = function(tree) { + if (tree.name && tree.name.identifierToken && tree.name.identifierToken.value == this.varName_) + this.usesGlobal = false; + return ScopeTransformer.prototype.transformFunctionDeclaration.apply(this, arguments); +} +GlobalUsageTransformer.prototype.getDoNotRecurse = function(tree) { + var doNotRecurse; + try { + doNotRecurse = ScopeTransformer.prototype.getDoNotRecurse.call(this, tree); + } + catch(e) {} + return doNotRecurse; +}; +GlobalUsageTransformer.prototype.transformBlock = function(tree) { + try { + tree = ScopeTransformer.prototype.transformBlock.call(this, tree); + } + catch(e) {} + return tree; +}; diff --git a/lib/npm.js b/lib/npm.js index be993aa..9092252 100644 --- a/lib/npm.js +++ b/lib/npm.js @@ -1,20 +1,30 @@ -var Promise = require('rsvp').Promise; -var asp = require('rsvp').denodeify; +var Promise = require('bluebird'); +var asp = require('bluebird').promisify; var request = require('request'); var zlib = require('zlib'); -var tar = require('tar'); +var tar = require('tar-fs'); var url = require('url'); var fs = require('graceful-fs'); var path = require('path'); var mkdirp = require('mkdirp'); +var peek = require('buffer-peek-stream'); var Npmrc = require('./npmrc'); var auth = require('./auth'); +var nodeSemver = require('semver'); var nodeConversion = require('./node-conversion'); var Npmrc = require('./npmrc'); var defaultRegistry = 'https://registry.npmjs.org'; +// Test whether the contents of buffer is gzipped +function isGzip(buffer) { + if (!buffer || buffer.length < 3) { + return false; + } + return buffer[0] === 0x1f && buffer[1] === 0x8b && buffer[2] === 0x08; +} + function clone(a) { var b = {}; for (var p in a) { @@ -31,19 +41,13 @@ function clone(a) { var NPMLocation = function(options, ui) { this.ui = ui; this.name = options.name; - - var npmrc = new Npmrc(); - - this.registryURL = function (scope) { - return scope && npmrc.getRegistry(scope) || options.registry || npmrc.getRegistry() || defaultRegistry; - }; - this.tmpDir = options.tmpDir; this.remote = options.remote; this.strictSSL = 'strictSSL' in options ? options.strictSSL : true; + this.maxRepoSize = 'maxRepoSize' in options ? options.maxRepoSize * 1024 * 1024 : 50000000; // cache versioning scheme used for patches - // this.versionString = options.versionString + '.1'; + this.versionString = options.versionString + '.2'; if (options.username && !options.auth) options.auth = auth.encodeCredentials(options.username, options.password); @@ -51,16 +55,50 @@ var NPMLocation = function(options, ui) { // delete options.username; // delete options.password; - if (options.authToken) - this.auth = { token: options.authToken }; - else if (options.auth) - this.auth = auth.decodeCredentials(options.auth); - else - this.auth = npmrc.getAuth(this.registryURL()); + var npmrc = new Npmrc(); - // only alwaysAuth when the registryURL is not the defaultRegistry - // otherwise we just auth for scopes - this.alwaysAuth = this.registryURL() != defaultRegistry; + this.registryURL = function (scope) { + return scope && npmrc.getRegistry(scope) || options.registry || npmrc.getRegistry() || defaultRegistry; + }; + + this.registryInfo = function (repo) { + // scope + var scope; + if (repo[0] === '@') { + var scopeMatch = repo.match(/^(@.*)\//); + if (scopeMatch[1]) { + scope = scopeMatch[1]; + } + } + + // url + var url = this.registryURL(scope); + + // auth + // only alwaysAuth when the registryURL is not the defaultRegistry + // otherwise we just auth for scopes + var authData; + var ca; + if (this.registryURL() != defaultRegistry || scope) { + authData = scope && npmrc.getAuth(url); + ca = npmrc.getCa(); + + if (!authData) { + if (options.authToken) + authData = { token: options.authToken }; + else if (options.auth) + authData = auth.decodeCredentials(options.auth); + else + authData = npmrc.getAuth(this.registryURL()); + } + } + + return { + url: url, + auth: authData, + ca: ca + }; + } }; NPMLocation.configure = function(config, ui) { @@ -127,7 +165,7 @@ NPMLocation.configure = function(config, ui) { }); }; -NPMLocation.packageFormat = /^@[^\/]+\/[^\/]+|^[^@\/][^\/]+/; +NPMLocation.packageNameFormats = ['@*/*', '*']; NPMLocation.prototype = { @@ -139,8 +177,6 @@ NPMLocation.prototype = { var latestKey = 'latest'; var repoPath = repo[0] == '@' ? '@' + encodeURIComponent(repo.substr(1)) : encodeURIComponent(repo); - var doAuth = this.alwaysAuth || repo[0] == '@'; - return asp(fs.readFile)(path.resolve(self.tmpDir, repo + '.json')) .then(function(lookupJSON) { lookupCache = JSON.parse(lookupJSON.toString()); @@ -150,21 +186,17 @@ NPMLocation.prototype = { throw e; }) .then(function() { - var scope; - if (repo[0] === '@') { - var scopeMatch = repo.match(/^(@.*)\//); - if (scopeMatch[1]) { - scope = scopeMatch[1]; - } - } + var registryInfo = self.registryInfo(repo); + return asp(request)(auth.injectRequestOptions({ - uri: self.registryURL(scope) + '/' + repoPath, + uri: registryInfo.url + '/' + repoPath, gzip: true, strictSSL: self.strictSSL, + gzip: true, headers: lookupCache ? { 'if-none-match': lookupCache.eTag } : {} - }, doAuth && self.auth)).then(function(res) { + }, registryInfo)).then(function(res) { if (res.statusCode == 304) return { versions: lookupCache.versions, latest: lookupCache.latest }; @@ -181,10 +213,11 @@ NPMLocation.prototype = { var versions = {}; var latest; var packageData; + var distTags; try { var json = JSON.parse(res.body); - var distTags = json['dist-tags'] || {}; + distTags = json['dist-tags'] || {}; packageData = json.versions; latest = distTags[latestKey]; } @@ -192,6 +225,10 @@ NPMLocation.prototype = { throw 'Unable to parse package.json'; } + for (var alias in distTags) + if (packageData[distTags[alias]]) + packageData[alias] = packageData[distTags[alias]]; + for (var v in packageData) { if (packageData[v].dist && packageData[v].dist.shasum) versions[v] = { @@ -213,7 +250,7 @@ NPMLocation.prototype = { return { versions: versions, latest: latest }; }, function(err) { - if (err.code == 'ENOTFOUND' && err.toString().indexOf('getaddrinfo') != -1 || err.code == 'ECONNRESET') { + if (err.code == 'ENOTFOUND' && err.toString().indexOf('getaddrinfo') != -1 || err.code == 'ECONNRESET' || err.code == 'ETIMEDOUT' || err.code == 'ESOCKETTIMEDOUT') { err.retriable = true; err.hideStack = true; } @@ -238,108 +275,87 @@ NPMLocation.prototype = { }); }, - getPackageConfig: function(repo, version, hash, pjson) { - if (!pjson) + getPackageConfig: function(repo, version, hash, packageConfig) { + if (!packageConfig) throw 'Package.json meta not provided in endpoint request'; - if (hash && pjson.dist.shasum != hash) + if (hash && packageConfig.dist.shasum != hash) throw 'Package.json lookup hash mismatch'; - return clone(pjson); + return clone(packageConfig); }, - processPackageConfig: function(pjson, packageName) { - if (pjson.jspmNodeConversion === false || pjson.jspmPackage) - return pjson; - - // peer dependencies are just dependencies in jspm - pjson.dependencies = pjson.dependencies || {}; - if (pjson.peerDependencies) { - for (d in pjson.peerDependencies) - pjson.dependencies[d] = pjson.peerDependencies[d]; - } - - // warn if using jspm-style dependencies at this point - for (var d in pjson.dependencies) - if (!pjson.dependencies[d].match(/^(https?|git)[:+]/) && pjson.dependencies[d].indexOf(':') > 0) - throw 'Package.json dependency %' + d + '% set to `' + pjson.dependencies[d] + '`, which is not a valid dependency format for npm.' - + '\nIt\'s advisable to publish jspm-style packages to GitHub or another `registry` so conventions are clear.'; - + '\nTo skip npm compatibility install with %jspm install ' + packageName + '-o "{jspmPackage: true}"%.' - - - pjson.dependencies = nodeConversion.parseDependencies(pjson.dependencies, this.ui); - - pjson.format = pjson.format || 'cjs'; + processPackageConfig: function(packageConfig, packageName) { + if (packageConfig.jspmNodeConversion === false || packageConfig.jspmPackage) + return packageConfig; + + packageConfig.dependencies = parseDependencies(packageConfig.dependencies, this.ui); + packageConfig.peerDependencies = parseDependencies(packageConfig.peerDependencies, this.ui); + packageConfig.optionalDependencies = parseDependencies(packageConfig.optionalDependencies, this.ui); + + // ensure optionalDependencies take preference over peer and normal dependencies + if (packageConfig.optionalDependencies) + Object.keys(packageConfig.optionalDependencies).forEach(function(dep) { + if (packageConfig.peerDependencies && packageConfig.peerDependencies[dep]) + delete packageConfig.peerDependencies[dep]; + if (packageConfig.dependencies && packageConfig.dependencies[dep]) + delete packageConfig.dependencies[dep]; + }); - if (pjson.main instanceof Array) - this.ui.log('warn', 'Package `' + packageName + '` has a main array, which has been ignored as it is not supported in Node and npm.'); + // ensure peerDependencies take preference over normal dependencies + if (packageConfig.peerDependencies) + Object.keys(packageConfig.peerDependencies).forEach(function(dep) { + if (packageConfig.dependencies[dep]) + delete packageConfig.dependencies[dep]; + }); - // json mains become plugins - if (pjson.main && typeof pjson.main == 'string' && pjson.main.substr(pjson.main.length - 5, 5) == '.json') { - pjson.main += '!systemjs-json'; - pjson.dependencies['systemjs-json'] = nodeConversion.jsonPlugin; + if (packageConfig.main instanceof Array) { + delete packageConfig.main; + this.ui.log('warn', 'Package `' + packageName + '` has a main array, which is not supported.'); } // ignore directory flattening for NodeJS, as npm doesn't do it // we do allow if there was an override through the jspm property though - if (!pjson.jspm || !pjson.jspm.directories) - delete pjson.directories; - - // ignore node_modules by default when processing - if (!(pjson.ignore instanceof Array)) - pjson.ignore = []; - pjson.ignore.push('node_modules'); - - if (pjson.files && pjson.files instanceof Array && pjson.files.indexOf('package.json') == -1) - pjson.files.push('package.json'); - - - // if there is a "browser" object, convert it into map config for browserify support - if (typeof pjson.browserify == 'string') - pjson.main = pjson.browserify; - if (typeof pjson.browser == 'string') - pjson.main = pjson.browser; - - if (typeof pjson.browser == 'object') { - pjson.map = pjson.map || {}; - for (var b in pjson.browser) { - var mapping = pjson.browser[b]; - - if (mapping === false) { - mapping = '@empty'; - } - else if (typeof mapping == 'string') { - if (b.substr(b.length - 3, 3) == '.js') - b = b.substr(0, b.length - 3); - if (mapping.substr(mapping.length - 3, 3) == '.js') - mapping = mapping.substr(0, mapping.length - 3); - - // we handle relative maps during the build phase - if (b.substr(0, 2) == './') - continue; - } - else - continue; + if (!packageConfig.jspm || !packageConfig.jspm.directories) { + delete packageConfig.directories; + } + else if (packageConfig.directories) { + if (!packageConfig.jspm.directories.lib) + delete packageConfig.directories.lib; + if (!packageConfig.jspm.directories.dist) + delete packageConfig.directories.dist; + } - pjson.map[b] = pjson.map[b] || mapping; - } + // keep the package.json and index.js around if we're doing files filtering + if (packageConfig.files && packageConfig.files instanceof Array) { + if (packageConfig.files.indexOf('package.json') == -1) + packageConfig.files.push('package.json'); + if (packageConfig.files.indexOf('index.js') == -1) + packageConfig.files.push('index.js'); + + packageConfig.files = packageConfig.files.map(function(file) { + // dist/* means dist/**/* in npm rules + if (file.substr(file.length - 2) == '/*') + return file + '*/*'; + return file; + }); } - return pjson; + return packageConfig; }, - download: function(repo, version, hash, versionData, outDir) { + download: function(repo, version, hash, versionData, targetDir) { var self = this; - var doAuth = this.alwaysAuth || repo[0] == '@'; + var registryInfo = self.registryInfo(repo); // Forcing protocol and port matching for tarballs on the same host as the // registry is taken from npm at // https://github.com/npm/npm/blob/50ce116baac8b6877434ace471104ec8587bab90/lib/cache/add-named.js#L196-L208 var tarball = url.parse(versionData.dist.tarball); - var registry = url.parse(this.registryURL()); + var registry = url.parse(registryInfo.url); - if (tarball.hostname === registry.hostname && tarball.protocol !== registry.protocol) { + if (tarball.hostname === registry.hostname && (tarball.protocol !== registry.protocol || tarball.port !== registry.port)) { tarball.protocol = registry.protocol; tarball.port = registry.port; } @@ -350,30 +366,41 @@ NPMLocation.prototype = { uri: tarball, headers: { 'accept': 'application/octet-stream' }, strictSSL: self.strictSSL - }, doAuth && self.auth)) + }, registryInfo)) .on('response', function(npmRes) { if (npmRes.statusCode != 200) return reject('Bad response code ' + npmRes.statusCode); - if (npmRes.headers['content-length'] > 50000000) - return reject('Response too large.'); + if (self.maxRepoSize > 0 && npmRes.headers['content-length'] > self.maxRepoSize) + return reject('Response too large. Consider increasing the limit: jspm config registries.npm.maxRepoSize 100'); npmRes.pause(); - var gzip = zlib.createGunzip(); + // Peek at the first 16 bytes of npmRes to check if the contents are gzipped + peek(npmRes, 16, function(err, bytes, stream) { + if (err) return reject(err); - npmRes - .pipe(gzip) - .pipe(tar.Extract({ - path: outDir, - strip: 1, - filter: function() { - return !this.type.match(/^.*Link$/); + // If the contents are gzipped pipe to gzip + if (isGzip(bytes)) { + var gzip = zlib.createGunzip(); + stream = stream.pipe(gzip); } - })) - .on('error', reject) - .on('end', resolve); + + // Unpack contents as a tar archive and save to targetDir + stream + .pipe(tar.extract(targetDir, { + // all dirs and files should be readable + dmode: 0555, + fmode: 0444, + strip: 1, + chown: false, + filter: function(_, header) { + return header.type !== 'file' && header.type !== 'directory' + } + }).on('finish', resolve).on('error', reject)) + .on('error', reject); + }); npmRes.resume(); }) @@ -388,13 +415,266 @@ NPMLocation.prototype = { }); }, - build: function(pjson, dir) { - if (pjson.jspmNodeConversion === false || pjson.jspmPackage) - return; + processPackage: function(packageConfig, packageName, packageDir) { + if (packageConfig.jspmNodeConversion === false || packageConfig.jspmPackage) + return packageConfig; // apply static conversions - return nodeConversion.convertPackage(pjson, dir); + return nodeConversion.convertPackage(packageConfig, packageName, packageDir, this.ui); } }; +// convert NodeJS or Bower dependencies into jspm-compatible dependencies +var githubRegEx = /^(git(\+[^:]+)?|https):\/\/github.com\/([^\/]+\/[^\/]+(\#.+|$))/; +var githubHttpRegEx = /^https?:\/\/github\.com\/([^\/]+\/[^\/]+)\/archive\/([^\/]+)\.(tar\.gz|zip)$/; +var githubShorthandRegEx = /^github:([^#\/]+\/[^#\/]+)(#.+)?$/; +var canonicalRegEx = /^[^@\/\:]+\:(@?[^@]+)(@[^@\/]*)?$/; +var protocolRegEx = /^[^\:\/]+:\/\//; +var semverRegEx = /^(\d+)(?:\.(\d+)(?:\.(\d+)(?:-([\da-z-]+(?:\.[\da-z-]+)*)(?:\+([\da-z-]+(?:\.[\da-z-]+)*))?)?)?)?$/i; +function parseDependencies(dependencies, ui) { + // do dependency parsing + var outDependencies = {}; + var process = function(d) { + var dep = dependencies[d]; + + var match, name, version = ''; + + // 1. git://github.com/name/repo.git#version -> github:name/repo@version + if ((match = dep.match(githubRegEx))) { + dep = match[3]; + name = 'github:' + dep.split('#')[0]; + version = dep.split('#')[1] || '*'; + if (version.substr(0, 1) == 'v' && version.substr(1).match(semverRegEx)) + version = version.substr(1); + if (name.substr(name.length - 4, 4) == '.git') + name = name.substr(0, name.length - 4); + ui.log('warn', 'npm dependency `' + name + '@' + version + '` will likely only work if its GitHub repo has %registry: npm% in its package.json'); + } + + // 2. https?://github.com/name/repo/archive/v?[semver].tar.gz -> github:name/repo@[semver] + else if ((match = dep.match(githubHttpRegEx))) { + name = 'github:' + match[1]; + version = match[2]; + if (version.substr(0, 1) == 'v' && version.substr(1).match(semverRegEx)) + version = version.substr(1); + } + + // 3. url:// or file: -> not supported + else if (dep.match(protocolRegEx) || dep.substr(0, 5) == 'file:') + throw 'npm dependency format ' + dep + ' not supported by jspm.'; + + // 4. github:package/name#version + else if ((match = dep.match(githubShorthandRegEx))) { + name = 'github:' + match[1]; + version = match[2] ? match[2].substr(1) : '*'; + if (name.indexOf('@') != -1) + ui.log('warn', 'npm dependency `' + name + '` uses a %@% version which is invalid in npm. Use %#% as the version separator or set %"registry": "jspm"% in the override for the install to work.'); + } + + // 5. name/repo#version -> github:name/repo@version + else if (dep.split('/').length == 2) { + name = 'github:' + dep.split('#')[0]; + version = dep.split('#')[1] || '*'; + } + + // 6. registry:package/name@version + else if ((match = dep.match(canonicalRegEx))) { + name = 'github:' + match[1]; + version = match[2] ? match[2].substr(1) : '*'; + } + + // 6. version -> name@version + else { + name = d; + version = dep; + } + + // otherwise, we convert an npm range into something jspm-compatible + // if it is an exact semver, or a tag, just use it directly + if (!nodeSemver.valid(version)) { + var range; + + // comma space is allowed on npm for some reason + version = version.replace(/, /g, ' '); + + if (!version || version == 'latest' || version == '*') + version = '*'; + + // if we have a semver or fuzzy range, just keep as-is + else if (version.indexOf(/[ <>=]/) != -1 || !version.substr(1).match(semverRegEx) || !version.substr(0, 1).match(/[\^\~]/)) + range = nodeSemver.validRange(version); + + if (range == '*') + version = '*'; + + else if (range) { + // if it has OR semantics, we only support the last range + if (range.indexOf('||') != -1) + range = range.split('||').pop(); + + var rangeParts = range.split(' '); + + // convert AND statements into a single lower bound and upper bound + // enforcing the lower bound as inclusive and the upper bound as exclusive + var lowerBound, upperBound, lEq, uEq; + for (var i = 0; i < rangeParts.length; i++) { + var part = rangeParts[i]; + var a = part.charAt(0); + var b = part.charAt(1); + + // get the version + var v = part; + if (b == '=') + v = part.substr(2); + else if (a == '>' || a == '<' || a == '=') + v = part.substr(1); + + // and the operator + var gt = a == '>'; + var lt = a == '<'; + + if (gt) { + // take the highest lower bound + if (!lowerBound || nodeSemver.gt(lowerBound, v)) { + lowerBound = v; + lEq = b == '='; + } + } + else if (lt) { + // take the lowest upper bound + if (!upperBound || nodeSemver.lt(upperBound, v)) { + upperBound = v; + uEq = b == '='; + } + } + else { + // equality + lowerBound = upperBound = (part.substr(0, 1) == '=' ? part.substr(1) : part); + lEq = uEq = true; + break; + } + } + + // for some reason nodeSemver adds "-0" when not appropriate + if (lowerBound && lowerBound.substr(lowerBound.length - 2, 2) == '-0') + lowerBound = lowerBound.substr(0, lowerBound.length - 2); + if (upperBound && upperBound.substr(upperBound.length - 2, 2) == '-0') + upperBound = upperBound.substr(0, upperBound.length - 2); + + var lowerSemver, upperSemver; + + if (lowerBound) { + lowerSemver = lowerBound.match(semverRegEx); + lowerSemver[1] = parseInt(lowerSemver[1], 10); + lowerSemver[2] = parseInt(lowerSemver[2], 10); + lowerSemver[3] = parseInt(lowerSemver[3], 10); + if (!lEq) { + //if (!lowerSemver[4]) + lowerSemver[4] = '0'; + // NB support incrementing existing preleases + } + } + + if (upperBound) { + upperSemver = upperBound.match(semverRegEx); + upperSemver[1] = parseInt(upperSemver[1], 10); + upperSemver[2] = parseInt(upperSemver[2], 10); + upperSemver[3] = parseInt(upperSemver[3], 10); + } + + if (!upperBound && !lowerBound) { + version = ''; + } + + // if not upperBound, then just treat as a wildcard + else if (!upperBound) { + version = '*'; + } + + // if no lowerBound, use the upperBound directly, with sensible decrementing if necessary + else if (!lowerBound) { + + if (uEq) { + version = upperBound; + } + + else { + if (!upperSemver[4]) { + lEq = true; + if (upperSemver[3] > 0) { + lowerSemver = [undefined, upperSemver[1], upperSemver[2], upperSemver[3] - 1]; + } + else if (upperSemver[2] > 0) { + lowerSemver = [undefined, upperSemver[1], upperSemver[2] - 1, 0]; + } + else if (upperSemver[1] > 0) { + lowerSemver = [undefined, upperSemver[1] - 1, 0, 0]; + } + } + else { + upperSemver[4] = undefined; + version = getVersion(upperSemver); + } + } + } + + if (upperSemver && lowerSemver) { + // if upper bound is inclusive, use it + if (uEq) + version = upperBound; + + // if upper bound is exact major + else if (upperSemver[2] === 0 && upperSemver[3] === 0 && !upperSemver[4]) { + + // if previous major is 0 + if (upperSemver[1] - 1 === 0) { + version = '0'; + } + else { + // if lower bound is major below, we are semver compatible + if (lowerSemver[1] == upperSemver[1] - 1) + version = '^' + getVersion(lowerSemver); + // otherwise we are semver compatible with the previous exact major + else + version = '^' + (upperSemver[1] - 1); + } + } + // if upper bound is exact minor + else if (upperSemver[3] === 0 && !upperSemver[4]) { + // if lower bound is minor below, we are fuzzy compatible + if (lowerSemver[2] == upperSemver[2] - 1) + version = '~' + getVersion(lowerSemver); + // otherwise we are fuzzy compatible with previous + else + version = '~' + upperSemver[1] + '.' + (upperSemver[2] - 1) + '.0'; + } + // if upper bound is exact version -> use exact + else { + if (lEq) + version = getVersion(lowerSemver); + else + throw 'Unable to translate npm version ' + version + ' into a jspm range.'; + } + } + } + } + + // replace ~x.y.0 with x.y shorthand + var shorthandMatch = version && version.match(/^~(\d+)\.(\d+)\.0$/) + if (shorthandMatch) + version = shorthandMatch[1] + '.' + shorthandMatch[2]; + + outDependencies[d] = name + (version ? '@' + version : ''); + }; + for (var d in dependencies) + process(d); + return outDependencies; +} +// export for unit testing +NPMLocation.parseDependencies = parseDependencies; + +function getVersion(semver) { + return semver[1] + '.' + semver[2] + '.' + semver[3] + (semver[4] ? '-' + semver[4] : ''); +} + module.exports = NPMLocation; diff --git a/lib/npmrc.js b/lib/npmrc.js index 3b4ccbd..56bde12 100644 --- a/lib/npmrc.js +++ b/lib/npmrc.js @@ -1,4 +1,4 @@ -var asp = require('rsvp').denodeify; +var asp = require('bluebird').promisify; var fs = require('graceful-fs'); var path = require('path'); var which = require('which'); @@ -14,6 +14,15 @@ function getFilepaths() { return paths; } +function getOptionEnvironmentVariable(option) { + var regex = /\$\{([\w]+)\}/m; + var result = option.match(regex); + if (result) + return process.env[result[1]] || option; + + return option; +} + function getOptionExact(contents, key) { var regex = new RegExp('^' + key + ' ?= ?(.+)', 'm'); var result; @@ -21,7 +30,7 @@ function getOptionExact(contents, key) { var content = contents[i]; result = content && content.match(regex); if (result) - return result[1]; + return getOptionEnvironmentVariable(result[1]); } } @@ -74,6 +83,18 @@ Npmrc.prototype.getAuth = function(registry) { }; }; +Npmrc.prototype.getCa = function(registry) { + if (!registry) + registry = this.getRegistry(); + + if (!this.initialized) + this.init(); + + var cafile = getOption(this.contents, 'cafile', registry); + if (cafile) + return fs.readFileSync(cafile); +}; + Npmrc.prototype.getRegistry = function(scope) { if (!this.initialized) this.init(); diff --git a/package.json b/package.json index 45bf920..213fff1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jspm-npm", - "version": "0.26.8", + "version": "0.30.5", "description": "jspm npm endpoint", "main": "npm.js", "scripts": { @@ -17,23 +17,22 @@ "url": "https://github.com/jspm/npm/issues" }, "dependencies": { - "glob": "^5.0.10", + "bluebird": "^3.0.5", + "buffer-peek-stream": "^1.0.1", "graceful-fs": "^4.1.3", "mkdirp": "^0.5.1", - "request": "~2.58.0", - "resolve": "^1.1.6", - "rmdir": "~1.1.0", - "rsvp": "^3.0.18", + "readdirp": "^2.0.0", + "request": "^2.58.0", "semver": "^5.0.1", - "systemjs-builder": "^0.15.0", - "tar": "~1.0.3", + "tar-fs": "^1.13.0", + "traceur": "0.0.105", "which": "^1.1.1" }, "homepage": "https://github.com/jspm/npm", "devDependencies": { "istanbul": "0.3.21", - "mocha": "2.3.3", - "unexpected": "9.16.0", - "unexpected-mitm": "7.7.3" + "mocha": "^3.4.2", + "unexpected": "^10.29.0", + "unexpected-mitm": "^10.0.0" } } diff --git a/test/auth.spec.js b/test/auth.spec.js index 77756d4..b45988b 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -98,8 +98,10 @@ describe('lib/auth', function () { }); it('should inject BasicAuth options into request options', function () { requestOptions = auth.injectRequestOptions(requestOptions, { - username: 'foo', - password: 'bar' + auth: { + username: 'foo', + password: 'bar' + } }) return expect(requestOptions, 'to satisfy', { auth: { @@ -110,7 +112,9 @@ describe('lib/auth', function () { }); it('should inject auth tokens into request options', function () { requestOptions = auth.injectRequestOptions(requestOptions, { - token: 'foobar' + auth: { + token: 'foobar' + } }) return expect(requestOptions, 'to satisfy', { headers: { @@ -123,7 +127,9 @@ describe('lib/auth', function () { 'X-Custom-Header': 'helloworld' }; requestOptions = auth.injectRequestOptions(requestOptions, { - token: 'foobar' + auth: { + token: 'foobar' + } }) return expect(requestOptions, 'to satisfy', { headers: { @@ -132,6 +138,16 @@ describe('lib/auth', function () { } }); }); + it('should inject certificate options into request options', function () { + requestOptions = auth.injectRequestOptions(requestOptions, { + ca: '-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----' + }) + return expect(requestOptions, 'to satisfy', { + agentOptions: { + ca: '-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----' + } + }); + }); }); describe('configureCredentials', function () { it('token based authentication chosen', function () { diff --git a/test/config.js b/test/config.js deleted file mode 100644 index e723b19..0000000 --- a/test/config.js +++ /dev/null @@ -1,349 +0,0 @@ -System.config({ - "defaultJSExtensions": true, - "transpiler": "traceur", - "paths": { - "github:*": "jspm_packages/github/*", - "npm:*": "jspm_packages/npm/*" - } -}); - -System.config({ - "map": { - "assert": "github:jspm/nodelibs-assert@0.1.0", - "buffer": "github:jspm/nodelibs-buffer@0.1.0", - "child_process": "github:jspm/nodelibs-child_process@0.1.0", - "cluster": "github:jspm/nodelibs-cluster@0.1.0", - "console": "github:jspm/nodelibs-console@0.1.0", - "constants": "github:jspm/nodelibs-constants@0.1.0", - "crypto": "github:jspm/nodelibs-crypto@0.1.0", - "dgram": "github:jspm/nodelibs-dgram@0.1.0", - "dns": "github:jspm/nodelibs-dns@0.1.0", - "domain": "github:jspm/nodelibs-domain@0.1.0", - "events": "github:jspm/nodelibs-events@0.1.1", - "fs": "github:jspm/nodelibs-fs@0.1.2", - "http": "github:jspm/nodelibs-http@1.7.1", - "https": "github:jspm/nodelibs-https@0.1.0", - "module": "github:jspm/nodelibs-module@0.1.0", - "net": "github:jspm/nodelibs-net@0.1.2", - "os": "github:jspm/nodelibs-os@0.1.0", - "path": "github:jspm/nodelibs-path@0.1.0", - "process": "github:jspm/nodelibs-process@0.1.1", - "punycode": "github:jspm/nodelibs-punycode@0.1.0", - "querystring": "github:jspm/nodelibs-querystring@0.1.0", - "readline": "github:jspm/nodelibs-readline@0.1.0", - "repl": "github:jspm/nodelibs-repl@0.1.0", - "stream": "github:jspm/nodelibs-stream@0.1.0", - "string_decoder": "github:jspm/nodelibs-string_decoder@0.1.0", - "timers": "github:jspm/nodelibs-timers@0.1.0", - "tls": "github:jspm/nodelibs-tls@0.1.0", - "traceur": "github:jmcriffey/bower-traceur@0.0.89", - "traceur-runtime": "github:jmcriffey/bower-traceur-runtime@0.0.89", - "tty": "github:jspm/nodelibs-tty@0.1.0", - "url": "github:jspm/nodelibs-url@0.1.0", - "util": "github:jspm/nodelibs-util@0.1.0", - "vm": "github:jspm/nodelibs-vm@0.1.0", - "zlib": "github:jspm/nodelibs-zlib@0.1.0", - "github:jspm/nodelibs-assert@0.1.0": { - "assert": "npm:assert@1.3.0" - }, - "github:jspm/nodelibs-buffer@0.1.0": { - "buffer": "npm:buffer@3.2.2" - }, - "github:jspm/nodelibs-console@0.1.0": { - "console-browserify": "npm:console-browserify@1.1.0" - }, - "github:jspm/nodelibs-constants@0.1.0": { - "constants-browserify": "npm:constants-browserify@0.0.1" - }, - "github:jspm/nodelibs-crypto@0.1.0": { - "crypto-browserify": "npm:crypto-browserify@3.9.14" - }, - "github:jspm/nodelibs-domain@0.1.0": { - "domain-browser": "npm:domain-browser@1.1.4" - }, - "github:jspm/nodelibs-events@0.1.1": { - "events": "npm:events@1.0.2" - }, - "github:jspm/nodelibs-http@1.7.1": { - "Base64": "npm:Base64@0.2.1", - "events": "github:jspm/nodelibs-events@0.1.1", - "inherits": "npm:inherits@2.0.1", - "stream": "github:jspm/nodelibs-stream@0.1.0", - "url": "github:jspm/nodelibs-url@0.1.0", - "util": "github:jspm/nodelibs-util@0.1.0" - }, - "github:jspm/nodelibs-https@0.1.0": { - "https-browserify": "npm:https-browserify@0.0.0" - }, - "github:jspm/nodelibs-net@0.1.2": { - "buffer": "github:jspm/nodelibs-buffer@0.1.0", - "crypto": "github:jspm/nodelibs-crypto@0.1.0", - "http": "github:jspm/nodelibs-http@1.7.1", - "net": "github:jspm/nodelibs-net@0.1.2", - "process": "github:jspm/nodelibs-process@0.1.1", - "stream": "github:jspm/nodelibs-stream@0.1.0", - "timers": "github:jspm/nodelibs-timers@0.1.0", - "util": "github:jspm/nodelibs-util@0.1.0" - }, - "github:jspm/nodelibs-os@0.1.0": { - "os-browserify": "npm:os-browserify@0.1.2" - }, - "github:jspm/nodelibs-path@0.1.0": { - "path-browserify": "npm:path-browserify@0.0.0" - }, - "github:jspm/nodelibs-process@0.1.1": { - "process": "npm:process@0.10.1" - }, - "github:jspm/nodelibs-punycode@0.1.0": { - "punycode": "npm:punycode@1.3.2" - }, - "github:jspm/nodelibs-querystring@0.1.0": { - "querystring": "npm:querystring@0.2.0" - }, - "github:jspm/nodelibs-stream@0.1.0": { - "stream-browserify": "npm:stream-browserify@1.0.0" - }, - "github:jspm/nodelibs-string_decoder@0.1.0": { - "string_decoder": "npm:string_decoder@0.10.31" - }, - "github:jspm/nodelibs-timers@0.1.0": { - "timers-browserify": "npm:timers-browserify@1.4.1" - }, - "github:jspm/nodelibs-tty@0.1.0": { - "tty-browserify": "npm:tty-browserify@0.0.0" - }, - "github:jspm/nodelibs-url@0.1.0": { - "url": "npm:url@0.10.3" - }, - "github:jspm/nodelibs-util@0.1.0": { - "util": "npm:util@0.10.3" - }, - "github:jspm/nodelibs-vm@0.1.0": { - "vm-browserify": "npm:vm-browserify@0.0.4" - }, - "github:jspm/nodelibs-zlib@0.1.0": { - "browserify-zlib": "npm:browserify-zlib@0.1.4" - }, - "npm:asn1.js@2.0.4": { - "assert": "github:jspm/nodelibs-assert@0.1.0", - "bn.js": "npm:bn.js@2.0.5", - "buffer": "github:jspm/nodelibs-buffer@0.1.0", - "inherits": "npm:inherits@2.0.1", - "minimalistic-assert": "npm:minimalistic-assert@1.0.0", - "vm": "github:jspm/nodelibs-vm@0.1.0" - }, - "npm:assert@1.3.0": { - "util": "npm:util@0.10.3" - }, - "npm:browserify-aes@1.0.1": { - "buffer": "github:jspm/nodelibs-buffer@0.1.0", - "create-hash": "npm:create-hash@1.1.1", - "crypto": "github:jspm/nodelibs-crypto@0.1.0", - "fs": "github:jspm/nodelibs-fs@0.1.2", - "inherits": "npm:inherits@2.0.1", - "stream": "github:jspm/nodelibs-stream@0.1.0", - "systemjs-json": "github:systemjs/plugin-json@0.1.0" - }, - "npm:browserify-rsa@2.0.1": { - "bn.js": "npm:bn.js@2.0.5", - "buffer": "github:jspm/nodelibs-buffer@0.1.0", - "constants": "github:jspm/nodelibs-constants@0.1.0", - "crypto": "github:jspm/nodelibs-crypto@0.1.0", - "randombytes": "npm:randombytes@2.0.1" - }, - "npm:browserify-sign@3.0.2": { - "bn.js": "npm:bn.js@2.0.5", - "browserify-rsa": "npm:browserify-rsa@2.0.1", - "buffer": "github:jspm/nodelibs-buffer@0.1.0", - "create-hash": "npm:create-hash@1.1.1", - "create-hmac": "npm:create-hmac@1.1.3", - "crypto": "github:jspm/nodelibs-crypto@0.1.0", - "elliptic": "npm:elliptic@3.1.0", - "inherits": "npm:inherits@2.0.1", - "parse-asn1": "npm:parse-asn1@3.0.1", - "stream": "github:jspm/nodelibs-stream@0.1.0", - "systemjs-json": "github:systemjs/plugin-json@0.1.0" - }, - "npm:browserify-zlib@0.1.4": { - "assert": "github:jspm/nodelibs-assert@0.1.0", - "buffer": "github:jspm/nodelibs-buffer@0.1.0", - "pako": "npm:pako@0.2.7", - "process": "github:jspm/nodelibs-process@0.1.1", - "readable-stream": "npm:readable-stream@1.1.13", - "util": "github:jspm/nodelibs-util@0.1.0" - }, - "npm:buffer@3.2.2": { - "base64-js": "npm:base64-js@0.0.8", - "ieee754": "npm:ieee754@1.1.6", - "is-array": "npm:is-array@1.0.1" - }, - "npm:console-browserify@1.1.0": { - "assert": "github:jspm/nodelibs-assert@0.1.0", - "date-now": "npm:date-now@0.1.4", - "util": "github:jspm/nodelibs-util@0.1.0" - }, - "npm:constants-browserify@0.0.1": { - "systemjs-json": "github:systemjs/plugin-json@0.1.0" - }, - "npm:core-util-is@1.0.1": { - "buffer": "github:jspm/nodelibs-buffer@0.1.0" - }, - "npm:create-ecdh@2.0.1": { - "bn.js": "npm:bn.js@2.0.5", - "buffer": "github:jspm/nodelibs-buffer@0.1.0", - "crypto": "github:jspm/nodelibs-crypto@0.1.0", - "elliptic": "npm:elliptic@3.1.0" - }, - "npm:create-hash@1.1.1": { - "buffer": "github:jspm/nodelibs-buffer@0.1.0", - "crypto": "github:jspm/nodelibs-crypto@0.1.0", - "fs": "github:jspm/nodelibs-fs@0.1.2", - "inherits": "npm:inherits@2.0.1", - "ripemd160": "npm:ripemd160@1.0.1", - "sha.js": "npm:sha.js@2.4.2", - "stream": "github:jspm/nodelibs-stream@0.1.0" - }, - "npm:create-hmac@1.1.3": { - "buffer": "github:jspm/nodelibs-buffer@0.1.0", - "create-hash": "npm:create-hash@1.1.1", - "crypto": "github:jspm/nodelibs-crypto@0.1.0", - "inherits": "npm:inherits@2.0.1", - "stream": "github:jspm/nodelibs-stream@0.1.0" - }, - "npm:crypto-browserify@3.9.14": { - "browserify-aes": "npm:browserify-aes@1.0.1", - "browserify-sign": "npm:browserify-sign@3.0.2", - "create-ecdh": "npm:create-ecdh@2.0.1", - "create-hash": "npm:create-hash@1.1.1", - "create-hmac": "npm:create-hmac@1.1.3", - "diffie-hellman": "npm:diffie-hellman@3.0.2", - "inherits": "npm:inherits@2.0.1", - "pbkdf2": "npm:pbkdf2@3.0.4", - "public-encrypt": "npm:public-encrypt@2.0.1", - "randombytes": "npm:randombytes@2.0.1" - }, - "npm:diffie-hellman@3.0.2": { - "bn.js": "npm:bn.js@2.0.5", - "buffer": "github:jspm/nodelibs-buffer@0.1.0", - "crypto": "github:jspm/nodelibs-crypto@0.1.0", - "miller-rabin": "npm:miller-rabin@2.0.1", - "randombytes": "npm:randombytes@2.0.1", - "systemjs-json": "github:systemjs/plugin-json@0.1.0" - }, - "npm:domain-browser@1.1.4": { - "events": "github:jspm/nodelibs-events@0.1.1" - }, - "npm:elliptic@3.1.0": { - "bn.js": "npm:bn.js@2.0.5", - "brorand": "npm:brorand@1.0.5", - "hash.js": "npm:hash.js@1.0.3", - "inherits": "npm:inherits@2.0.1", - "systemjs-json": "github:systemjs/plugin-json@0.1.0" - }, - "npm:hash.js@1.0.3": { - "inherits": "npm:inherits@2.0.1" - }, - "npm:https-browserify@0.0.0": { - "http": "github:jspm/nodelibs-http@1.7.1" - }, - "npm:inherits@2.0.1": { - "util": "github:jspm/nodelibs-util@0.1.0" - }, - "npm:miller-rabin@2.0.1": { - "bn.js": "npm:bn.js@2.0.5", - "brorand": "npm:brorand@1.0.5" - }, - "npm:os-browserify@0.1.2": { - "os": "github:jspm/nodelibs-os@0.1.0" - }, - "npm:pako@0.2.7": { - "buffer": "github:jspm/nodelibs-buffer@0.1.0", - "process": "github:jspm/nodelibs-process@0.1.1" - }, - "npm:parse-asn1@3.0.1": { - "asn1.js": "npm:asn1.js@2.0.4", - "browserify-aes": "npm:browserify-aes@1.0.1", - "buffer": "github:jspm/nodelibs-buffer@0.1.0", - "create-hash": "npm:create-hash@1.1.1", - "pbkdf2": "npm:pbkdf2@3.0.4", - "systemjs-json": "github:systemjs/plugin-json@0.1.0" - }, - "npm:path-browserify@0.0.0": { - "process": "github:jspm/nodelibs-process@0.1.1" - }, - "npm:pbkdf2@3.0.4": { - "buffer": "github:jspm/nodelibs-buffer@0.1.0", - "child_process": "github:jspm/nodelibs-child_process@0.1.0", - "create-hmac": "npm:create-hmac@1.1.3", - "crypto": "github:jspm/nodelibs-crypto@0.1.0", - "path": "github:jspm/nodelibs-path@0.1.0", - "process": "github:jspm/nodelibs-process@0.1.1", - "systemjs-json": "github:systemjs/plugin-json@0.1.0" - }, - "npm:process@0.11.1": { - "assert": "github:jspm/nodelibs-assert@0.1.0" - }, - "npm:public-encrypt@2.0.1": { - "bn.js": "npm:bn.js@2.0.5", - "browserify-rsa": "npm:browserify-rsa@2.0.1", - "buffer": "github:jspm/nodelibs-buffer@0.1.0", - "create-hash": "npm:create-hash@1.1.1", - "crypto": "github:jspm/nodelibs-crypto@0.1.0", - "parse-asn1": "npm:parse-asn1@3.0.1", - "randombytes": "npm:randombytes@2.0.1" - }, - "npm:punycode@1.3.2": { - "process": "github:jspm/nodelibs-process@0.1.1" - }, - "npm:randombytes@2.0.1": { - "buffer": "github:jspm/nodelibs-buffer@0.1.0", - "crypto": "github:jspm/nodelibs-crypto@0.1.0", - "process": "github:jspm/nodelibs-process@0.1.1" - }, - "npm:readable-stream@1.1.13": { - "buffer": "github:jspm/nodelibs-buffer@0.1.0", - "core-util-is": "npm:core-util-is@1.0.1", - "events": "github:jspm/nodelibs-events@0.1.1", - "inherits": "npm:inherits@2.0.1", - "isarray": "npm:isarray@0.0.1", - "process": "github:jspm/nodelibs-process@0.1.1", - "stream-browserify": "npm:stream-browserify@1.0.0", - "string_decoder": "npm:string_decoder@0.10.31" - }, - "npm:ripemd160@1.0.1": { - "buffer": "github:jspm/nodelibs-buffer@0.1.0", - "process": "github:jspm/nodelibs-process@0.1.1" - }, - "npm:sha.js@2.4.2": { - "buffer": "github:jspm/nodelibs-buffer@0.1.0", - "fs": "github:jspm/nodelibs-fs@0.1.2", - "inherits": "npm:inherits@2.0.1", - "process": "github:jspm/nodelibs-process@0.1.1" - }, - "npm:stream-browserify@1.0.0": { - "events": "github:jspm/nodelibs-events@0.1.1", - "inherits": "npm:inherits@2.0.1", - "readable-stream": "npm:readable-stream@1.1.13" - }, - "npm:string_decoder@0.10.31": { - "buffer": "github:jspm/nodelibs-buffer@0.1.0" - }, - "npm:timers-browserify@1.4.1": { - "process": "npm:process@0.11.1" - }, - "npm:url@0.10.3": { - "assert": "github:jspm/nodelibs-assert@0.1.0", - "punycode": "npm:punycode@1.3.2", - "querystring": "npm:querystring@0.2.0", - "util": "github:jspm/nodelibs-util@0.1.0" - }, - "npm:util@0.10.3": { - "inherits": "npm:inherits@2.0.1", - "process": "github:jspm/nodelibs-process@0.1.1" - }, - "npm:vm-browserify@0.0.4": { - "indexof": "npm:indexof@0.0.1" - } - } -}); - diff --git a/test/fixtures/deps-transformer/process-detection1.js b/test/fixtures/deps-transformer/process-detection1.js new file mode 100644 index 0000000..3b93d68 --- /dev/null +++ b/test/fixtures/deps-transformer/process-detection1.js @@ -0,0 +1,51 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule keyMirror + * @typechecks static-only + */ + +'use strict'; + +var invariant = require("./invariant"); + +/** + * Constructs an enumeration with keys equal to their value. + * + * For example: + * + * var COLORS = keyMirror({blue: null, red: null}); + * var myColor = COLORS.blue; + * var isColorValid = !!COLORS[myColor]; + * + * The last line could not be performed if the values of the generated enum were + * not equal to their keys. + * + * Input: {key1: val1, key2: val2} + * Output: {key1: key1, key2: key2} + * + * @param {object} obj + * @return {object} + */ +var keyMirror = function(obj) { + var ret = {}; + var key; + ("production" !== process.env.NODE_ENV ? invariant( + obj instanceof Object && !Array.isArray(obj), + 'keyMirror(...): Argument must be an object.' + ) : invariant(obj instanceof Object && !Array.isArray(obj))); + for (key in obj) { + if (!obj.hasOwnProperty(key)) { + continue; + } + ret[key] = key; + } + return ret; +}; + +module.exports = keyMirror; diff --git a/test/fixtures/deps-transformer/process-detection2.js b/test/fixtures/deps-transformer/process-detection2.js new file mode 100644 index 0000000..55aa52a --- /dev/null +++ b/test/fixtures/deps-transformer/process-detection2.js @@ -0,0 +1,3 @@ +(function(p) { + console.log(p); +})(process); \ No newline at end of file diff --git a/test/jspm.browser.js b/test/jspm.browser.js new file mode 100644 index 0000000..673d763 --- /dev/null +++ b/test/jspm.browser.js @@ -0,0 +1,6 @@ +SystemJS.config({ + paths: { + "github:*": "/jspm_packages/github/*", + "npm:*": "/jspm_packages/npm/*" + } +}); \ No newline at end of file diff --git a/test/jspm.js b/test/jspm.js new file mode 100644 index 0000000..448e704 --- /dev/null +++ b/test/jspm.js @@ -0,0 +1,325 @@ +SystemJS.config({ + packageConfigPaths: [ + "github:*/*.json", + "npm:@*/*.json", + "npm:*.json" + ], + transpiler: "traceur", + paths: { + "github:*": "jspm_packages/github/*", + "npm:*": "jspm_packages/npm/*" + }, + globalEvaluationScope: false, + + map: { + "assert": "github:jspm/nodelibs-assert@0.2.0-alpha", + "buffer": "github:jspm/nodelibs-buffer@0.2.0-alpha", + "child_process": "github:jspm/nodelibs-child_process@0.2.0-alpha", + "cluster": "github:jspm/nodelibs-cluster@0.2.0-alpha", + "console": "github:jspm/nodelibs-console@0.2.0-alpha", + "constants": "github:jspm/nodelibs-constants@0.2.0-alpha", + "crypto": "github:jspm/nodelibs-crypto@0.2.0-alpha", + "dgram": "github:jspm/nodelibs-dgram@0.2.0-alpha", + "dns": "github:jspm/nodelibs-dns@0.2.0-alpha", + "domain": "github:jspm/nodelibs-domain@0.2.0-alpha", + "events": "github:jspm/nodelibs-events@0.2.0-alpha", + "fs": "github:jspm/nodelibs-fs@0.2.0-alpha", + "http": "github:jspm/nodelibs-http@0.2.0-alpha", + "https": "github:jspm/nodelibs-https@0.2.0-alpha", + "module": "github:jspm/nodelibs-module@0.2.0-alpha", + "net": "github:jspm/nodelibs-net@0.2.0-alpha", + "nodelibs": "github:jspm/nodelibs@0.1.9", + "os": "github:jspm/nodelibs-os@0.2.0-alpha", + "path": "github:jspm/nodelibs-path@0.2.0-alpha", + "process": "github:jspm/nodelibs-process@0.2.0-alpha", + "punycode": "github:jspm/nodelibs-punycode@0.2.0-alpha", + "querystring": "github:jspm/nodelibs-querystring@0.2.0-alpha", + "readline": "github:jspm/nodelibs-readline@0.2.0-alpha", + "repl": "github:jspm/nodelibs-repl@0.2.0-alpha", + "stream": "github:jspm/nodelibs-stream@0.2.0-alpha", + "string_decoder": "github:jspm/nodelibs-string_decoder@0.2.0-alpha", + "timers": "github:jspm/nodelibs-timers@0.2.0-alpha", + "tls": "github:jspm/nodelibs-tls@0.2.0-alpha", + "traceur": "github:jmcriffey/bower-traceur@0.0.89", + "traceur-runtime": "github:jmcriffey/bower-traceur-runtime@0.0.89", + "tty": "github:jspm/nodelibs-tty@0.2.0-alpha", + "url": "github:jspm/nodelibs-url@0.2.0-alpha", + "util": "github:jspm/nodelibs-util@0.2.0-alpha", + "vm": "github:jspm/nodelibs-vm@0.2.0-alpha", + "zlib": "github:jspm/nodelibs-zlib@0.2.0-alpha" + }, + + packages: { + "github:jspm/nodelibs-buffer@0.2.0-alpha": { + "map": { + "buffer-browserify": "npm:buffer@4.0.0" + } + }, + "github:jspm/nodelibs-crypto@0.2.0-alpha": { + "map": { + "crypto-browserify": "npm:crypto-browserify@3.11.0" + } + }, + "github:jspm/nodelibs-domain@0.2.0-alpha": { + "map": { + "domain-browserify": "npm:domain-browser@1.1.7" + } + }, + "github:jspm/nodelibs-http@0.2.0-alpha": { + "map": { + "http-browserify": "npm:stream-http@2.0.3" + } + }, + "github:jspm/nodelibs-os@0.2.0-alpha": { + "map": { + "os-browserify": "npm:os-browserify@0.2.0" + } + }, + "github:jspm/nodelibs-punycode@0.2.0-alpha": { + "map": { + "punycode-browserify": "npm:punycode@1.3.2" + } + }, + "github:jspm/nodelibs-stream@0.2.0-alpha": { + "map": { + "stream-browserify": "npm:stream-browserify@2.0.1" + } + }, + "github:jspm/nodelibs-string_decoder@0.2.0-alpha": { + "map": { + "string_decoder-browserify": "npm:string_decoder@0.10.31" + } + }, + "github:jspm/nodelibs-timers@0.2.0-alpha": { + "map": { + "timers-browserify": "npm:timers-browserify@1.4.2" + } + }, + "github:jspm/nodelibs-url@0.2.0-alpha": { + "map": { + "url-browserify": "npm:url@0.11.0" + } + }, + "github:jspm/nodelibs-zlib@0.2.0-alpha": { + "map": { + "zlib-browserify": "npm:browserify-zlib@0.1.4" + } + }, + "npm:asn1.js@4.3.0": { + "map": { + "bn.js": "npm:bn.js@4.6.2", + "inherits": "npm:inherits@2.0.1", + "minimalistic-assert": "npm:minimalistic-assert@1.0.0" + } + }, + "npm:browserify-aes@1.0.5": { + "map": { + "buffer-xor": "npm:buffer-xor@1.0.3", + "cipher-base": "npm:cipher-base@1.0.2", + "create-hash": "npm:create-hash@1.1.1", + "evp_bytestokey": "npm:evp_bytestokey@1.0.0", + "inherits": "npm:inherits@2.0.1" + } + }, + "npm:browserify-cipher@1.0.0": { + "map": { + "browserify-aes": "npm:browserify-aes@1.0.5", + "browserify-des": "npm:browserify-des@1.0.0", + "evp_bytestokey": "npm:evp_bytestokey@1.0.0" + } + }, + "npm:browserify-des@1.0.0": { + "map": { + "cipher-base": "npm:cipher-base@1.0.2", + "des.js": "npm:des.js@1.0.0", + "inherits": "npm:inherits@2.0.1" + } + }, + "npm:browserify-rsa@4.0.0": { + "map": { + "bn.js": "npm:bn.js@4.6.2", + "randombytes": "npm:randombytes@2.0.1" + } + }, + "npm:browserify-sign@4.0.0": { + "map": { + "bn.js": "npm:bn.js@4.6.2", + "browserify-rsa": "npm:browserify-rsa@4.0.0", + "create-hash": "npm:create-hash@1.1.1", + "create-hmac": "npm:create-hmac@1.1.3", + "elliptic": "npm:elliptic@6.0.2", + "inherits": "npm:inherits@2.0.1", + "parse-asn1": "npm:parse-asn1@5.0.0" + } + }, + "npm:browserify-zlib@0.1.4": { + "map": { + "pako": "npm:pako@0.2.8", + "readable-stream": "npm:readable-stream@2.0.5" + } + }, + "npm:buffer@4.0.0": { + "map": { + "base64-js": "npm:base64-js@0.0.8", + "ieee754": "npm:ieee754@1.1.6", + "isarray": "npm:isarray@1.0.0" + } + }, + "npm:cipher-base@1.0.2": { + "map": { + "inherits": "npm:inherits@2.0.1" + } + }, + "npm:create-ecdh@4.0.0": { + "map": { + "bn.js": "npm:bn.js@4.6.2", + "elliptic": "npm:elliptic@6.0.2" + } + }, + "npm:create-hash@1.1.1": { + "map": { + "inherits": "npm:inherits@2.0.1", + "ripemd160": "npm:ripemd160@1.0.1", + "sha.js": "npm:sha.js@2.4.2" + } + }, + "npm:create-hmac@1.1.3": { + "map": { + "create-hash": "npm:create-hash@1.1.1", + "inherits": "npm:inherits@2.0.1" + } + }, + "npm:crypto-browserify@3.11.0": { + "map": { + "browserify-cipher": "npm:browserify-cipher@1.0.0", + "browserify-sign": "npm:browserify-sign@4.0.0", + "create-ecdh": "npm:create-ecdh@4.0.0", + "create-hash": "npm:create-hash@1.1.1", + "create-hmac": "npm:create-hmac@1.1.3", + "diffie-hellman": "npm:diffie-hellman@5.0.0", + "inherits": "npm:inherits@2.0.1", + "pbkdf2": "npm:pbkdf2@3.0.4", + "public-encrypt": "npm:public-encrypt@4.0.0", + "randombytes": "npm:randombytes@2.0.1" + } + }, + "npm:des.js@1.0.0": { + "map": { + "inherits": "npm:inherits@2.0.1", + "minimalistic-assert": "npm:minimalistic-assert@1.0.0" + } + }, + "npm:diffie-hellman@5.0.0": { + "map": { + "bn.js": "npm:bn.js@4.6.2", + "miller-rabin": "npm:miller-rabin@4.0.0", + "randombytes": "npm:randombytes@2.0.1" + } + }, + "npm:elliptic@6.0.2": { + "map": { + "bn.js": "npm:bn.js@4.6.2", + "brorand": "npm:brorand@1.0.5", + "hash.js": "npm:hash.js@1.0.3", + "inherits": "npm:inherits@2.0.1" + } + }, + "npm:evp_bytestokey@1.0.0": { + "map": { + "create-hash": "npm:create-hash@1.1.1" + } + }, + "npm:hash.js@1.0.3": { + "map": { + "inherits": "npm:inherits@2.0.1" + } + }, + "npm:inherits@2.0.1": { + "map": {} + }, + "npm:miller-rabin@4.0.0": { + "map": { + "bn.js": "npm:bn.js@4.6.2", + "brorand": "npm:brorand@1.0.5" + } + }, + "npm:parse-asn1@5.0.0": { + "map": { + "asn1.js": "npm:asn1.js@4.3.0", + "browserify-aes": "npm:browserify-aes@1.0.5", + "create-hash": "npm:create-hash@1.1.1", + "evp_bytestokey": "npm:evp_bytestokey@1.0.0", + "pbkdf2": "npm:pbkdf2@3.0.4" + } + }, + "npm:pbkdf2@3.0.4": { + "map": { + "create-hmac": "npm:create-hmac@1.1.3", + "systemjs-json": "github:systemjs/plugin-json@0.1.0" + } + }, + "npm:process@0.11.1": { + "map": {} + }, + "npm:public-encrypt@4.0.0": { + "map": { + "bn.js": "npm:bn.js@4.6.2", + "browserify-rsa": "npm:browserify-rsa@4.0.0", + "create-hash": "npm:create-hash@1.1.1", + "parse-asn1": "npm:parse-asn1@5.0.0", + "randombytes": "npm:randombytes@2.0.1" + } + }, + "npm:punycode@1.3.2": { + "map": {} + }, + "npm:randombytes@2.0.1": { + "map": {} + }, + "npm:readable-stream@2.0.5": { + "map": { + "core-util-is": "npm:core-util-is@1.0.2", + "inherits": "npm:inherits@2.0.1", + "isarray": "npm:isarray@0.0.1", + "process-nextick-args": "npm:process-nextick-args@1.0.6", + "string_decoder": "npm:string_decoder@0.10.31", + "util-deprecate": "npm:util-deprecate@1.0.2" + } + }, + "npm:ripemd160@1.0.1": { + "map": {} + }, + "npm:sha.js@2.4.2": { + "map": { + "inherits": "npm:inherits@2.0.1" + } + }, + "npm:stream-browserify@2.0.1": { + "map": { + "inherits": "npm:inherits@2.0.1", + "readable-stream": "npm:readable-stream@2.0.5" + } + }, + "npm:stream-http@2.0.3": { + "map": { + "builtin-status-codes": "npm:builtin-status-codes@1.0.0", + "inherits": "npm:inherits@2.0.1", + "xtend": "npm:xtend@4.0.1" + } + }, + "npm:string_decoder@0.10.31": { + "map": {} + }, + "npm:timers-browserify@1.4.2": { + "map": { + "process": "npm:process@0.11.1" + } + }, + "npm:url@0.11.0": { + "map": { + "punycode": "npm:punycode@1.3.2", + "querystring": "npm:querystring@0.2.0" + } + } + } +}); diff --git a/test/mocha.opts b/test/mocha.opts index 9e399c3..13fc110 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,2 +1,3 @@ --reporter spec test/auth.spec.js +test/npmrc.spec.js diff --git a/test/npmrc.spec.js b/test/npmrc.spec.js new file mode 100644 index 0000000..f729adf --- /dev/null +++ b/test/npmrc.spec.js @@ -0,0 +1,80 @@ +var expect = require('unexpected') + .clone() + .use(require('unexpected-mitm')); +var Npmrc = require('../lib/npmrc'); +var fs = require('fs'); +var path = require('path'); + + +function extend(a, b) { + for (var p in b) + a[p] = b[p]; + return a; +} + +function FileMock(path) { + this.path = path; +}; + +FileMock.prototype.write = function(content) { + fs.writeFileSync(this.path, content); + return this; +}; + +FileMock.prototype.unlink = function() { + fs.unlinkSync(this.path); +}; + +describe('lib/npmrc', function () { + + describe('getCa()', function () { + var _jspmConfigPath; + var npmrc; + + beforeEach(function() { + _jspmConfigPath = process.env.jspmConfigPath; + npmrc = new Npmrc(); + }); + + afterEach(function() { + if (_jspmConfigPath) + process.env.jspmConfigPath = _jspmConfigPath; + }); + + it('should honor cafile property', function () { + var npmrcFile; + var caFile; + var ca; + + delete process.env.jspmConfigPath; + caFile = new FileMock(path.resolve(process.cwd(), 'ca.crt')) + .write('ca certificate'); + npmrcFile = new FileMock(path.resolve(process.cwd(), '.npmrc')) + .write('cafile=' + caFile.path); + + + var ca = npmrc.getCa(); + + + caFile.unlink(); + npmrcFile.unlink(); + return expect(ca, 'when decoded as', 'utf-8', 'to equal', 'ca certificate'); + }); + + it('should have empty ca', function () { + var npmrcFile; + var ca; + + delete process.env.jspmConfigPath; + npmrcFile = new FileMock(path.resolve(process.cwd(), '.npmrc')) + .write('# empty config'); + + + var ca = npmrc.getCa(); + + + npmrcFile.unlink(); + return expect(ca, 'to be undefined'); + }); + }); +}); diff --git a/test/package.json b/test/package.json index 7f53526..2184282 100644 --- a/test/package.json +++ b/test/package.json @@ -3,43 +3,81 @@ "directories": { "lib": "lib" }, - "dependencies": { - "assert": "jspm/nodelibs-assert@^0.1.0", - "buffer": "jspm/nodelibs-buffer@^0.1.0", - "child_process": "jspm/nodelibs-child_process@^0.1.0", - "cluster": "jspm/nodelibs-cluster@^0.1.0", - "console": "jspm/nodelibs-console@^0.1.0", - "constants": "jspm/nodelibs-constants@^0.1.0", - "crypto": "jspm/nodelibs-crypto@^0.1.0", - "dgram": "jspm/nodelibs-dgram@^0.1.0", - "dns": "jspm/nodelibs-dns@^0.1.0", - "domain": "jspm/nodelibs-domain@^0.1.0", - "events": "jspm/nodelibs-events@^0.1.1", - "fs": "jspm/nodelibs-fs@^0.1.1", - "http": "jspm/nodelibs-http@^1.7.0", - "https": "jspm/nodelibs-https@^0.1.0", - "module": "jspm/nodelibs-module@^0.1.0", - "net": "jspm/nodelibs-net@^0.1.2", - "os": "jspm/nodelibs-os@^0.1.0", - "path": "jspm/nodelibs-path@^0.1.0", - "process": "jspm/nodelibs-process@^0.1.1", - "punycode": "jspm/nodelibs-punycode@^0.1.0", - "querystring": "jspm/nodelibs-querystring@^0.1.0", - "readline": "jspm/nodelibs-readline@^0.1.0", - "repl": "jspm/nodelibs-repl@^0.1.0", - "stream": "jspm/nodelibs-stream@^0.1.0", - "string_decoder": "jspm/nodelibs-string_decoder@^0.1.0", - "timers": "jspm/nodelibs-timers@^0.1.0", - "tls": "jspm/nodelibs-tls@^0.1.0", - "tty": "jspm/nodelibs-tty@^0.1.0", - "url": "jspm/nodelibs-url@^0.1.0", - "util": "jspm/nodelibs-util@^0.1.0", - "vm": "jspm/nodelibs-vm@^0.1.0", - "zlib": "jspm/nodelibs-zlib@^0.1.0" - }, - "devDependencies": { + "dependencies": {}, + "devDependencies": {}, + "peerDependencies": { + "assert": "jspm/nodelibs-assert@^0.2.0-alpha", + "buffer": "jspm/nodelibs-buffer@^0.2.0-alpha", + "child_process": "jspm/nodelibs-child_process@^0.2.0-alpha", + "cluster": "jspm/nodelibs-cluster@^0.2.0-alpha", + "console": "jspm/nodelibs-console@^0.2.0-alpha", + "constants": "jspm/nodelibs-constants@^0.2.0-alpha", + "crypto": "jspm/nodelibs-crypto@^0.2.0-alpha", + "dgram": "jspm/nodelibs-dgram@^0.2.0-alpha", + "dns": "jspm/nodelibs-dns@^0.2.0-alpha", + "domain": "jspm/nodelibs-domain@^0.2.0-alpha", + "events": "jspm/nodelibs-events@^0.2.0-alpha", + "fs": "jspm/nodelibs-fs@^0.2.0-alpha", + "http": "jspm/nodelibs-http@^0.2.0-alpha", + "https": "jspm/nodelibs-https@^0.2.0-alpha", + "module": "jspm/nodelibs-module@^0.2.0-alpha", + "net": "jspm/nodelibs-net@^0.2.0-alpha", + "nodelibs": "jspm/nodelibs@^0.1.0", + "os": "jspm/nodelibs-os@^0.2.0-alpha", + "path": "jspm/nodelibs-path@^0.2.0-alpha", + "process": "jspm/nodelibs-process@^0.2.0-alpha", + "punycode": "jspm/nodelibs-punycode@^0.2.0-alpha", + "querystring": "jspm/nodelibs-querystring@^0.2.0-alpha", + "readline": "jspm/nodelibs-readline@^0.2.0-alpha", + "repl": "jspm/nodelibs-repl@^0.2.0-alpha", + "stream": "jspm/nodelibs-stream@^0.2.0-alpha", + "string_decoder": "jspm/nodelibs-string_decoder@^0.2.0-alpha", + "timers": "jspm/nodelibs-timers@^0.2.0-alpha", + "tls": "jspm/nodelibs-tls@^0.2.0-alpha", "traceur": "jmcriffey/bower-traceur@0.0.89", - "traceur-runtime": "jmcriffey/bower-traceur-runtime@0.0.89" + "traceur-runtime": "jmcriffey/bower-traceur-runtime@0.0.89", + "tty": "jspm/nodelibs-tty@^0.2.0-alpha", + "url": "jspm/nodelibs-url@^0.2.0-alpha", + "util": "jspm/nodelibs-util@^0.2.0-alpha", + "vm": "jspm/nodelibs-vm@^0.2.0-alpha", + "zlib": "jspm/nodelibs-zlib@^0.2.0-alpha" + }, + "overrides": { + "github:jmcriffey/bower-traceur-runtime@0.0.89": { + "format": "global", + "meta": { + "traceur-runtime.js": { + "exports": "$traceurRuntime" + } + } + }, + "github:jmcriffey/bower-traceur@0.0.89": { + "format": "global", + "meta": { + "traceur.js": { + "exports": "traceur" + } + } + }, + "npm:base64-js@0.0.8": { + "ignore": [ + "bench" + ] + }, + "npm:browserify-zlib@0.1.4": { + "dependencies": { + "readable-stream": "^2.0.2", + "pako": "~0.2.0" + }, + "map": { + "_stream_transform": "readable-stream/transform" + } + }, + "npm:inherits@2.0.1": { + "ignore": [ + "test.js" + ] + } }, "jspm": true } diff --git a/test/test-node.js b/test/test-node.js index 6427b59..48825da 100644 --- a/test/test-node.js +++ b/test/test-node.js @@ -18,7 +18,7 @@ assert(cluster.fork); assert(dgram.Socket); assert(dns.lookup); assert(module.Module); -assert(net.createServer); +assert(net.isIP('1.1.1.1'), true); assert(readline.cursorTo); assert(repl.start); assert(tls.connect); diff --git a/test/test-shared.js b/test/test-shared.js index 6010d97..235988e 100644 --- a/test/test-shared.js +++ b/test/test-shared.js @@ -7,7 +7,6 @@ var domain = require('domain'); var events = require('events'); var http = require('http'); var https = require('https'); -var net = require('net'); var os = require('os'); var path = require('path'); var process = require('process'); @@ -37,8 +36,6 @@ d.run(function() { assert(http.get); assert(https.get); - assert(net.isIP('1.1.1.1'), true); - assert(os.platform()); assert(path.join('one', 'two'), 'one' + path.sep + 'two'); diff --git a/test/test.html b/test/test.html index 6e2a192..66bea24 100644 --- a/test/test.html +++ b/test/test.html @@ -1,6 +1,7 @@ - + + \ No newline at end of file diff --git a/test/unit.js b/test/unit.js index 24bc6ef..89f4e01 100644 --- a/test/unit.js +++ b/test/unit.js @@ -1,10 +1,10 @@ -var nodeConversion = require('../lib/node-conversion'); - +var parseDependencies = require('../lib/npm').parseDependencies; +var fs = require('fs'); function testDependency(name, value, expectedName, expectedValue) { var deps = {}; deps[name] = value; - deps = nodeConversion.parseDependencies(deps); + deps = parseDependencies(deps); for (var p in deps) { if (p != expectedName) @@ -16,25 +16,30 @@ function testDependency(name, value, expectedName, expectedValue) { testDependency('react', '0.1.1', 'react', 'react@0.1.1'); testDependency('react', '=0.1.1', 'react', 'react@0.1.1'); -testDependency('react', '<0.12', 'react', 'react@0.11.0'); -testDependency('react', '<0.12.0', 'react', 'react@0.11.0'); +testDependency('react', '<0.12', 'react', 'react@0.11'); +testDependency('react', '<0.12.0', 'react', 'react@0.11'); testDependency('react', '~0.1.1', 'react', 'react@~0.1.1'); testDependency('react', '^0.12', 'react', 'react@^0.12'); -testDependency('react', '0.12.x', 'react', 'react@~0.12.0'); +testDependency('react', '0.12', 'react', 'react@0.12'); +testDependency('react', '0.12.x', 'react', 'react@0.12'); testDependency('react', '0.x', 'react', 'react@0'); testDependency('react', '>=0.12.0', 'react', 'react@*'); +testDependency('react', '<2.3.4 >=2.3.3', 'react', 'react@2.3.3'); // Scoped testDependency('@scoped/react', '0.11.0', '@scoped/react', '@scoped/react@0.11.0'); testDependency('@scoped/react', '=0.11.0', '@scoped/react', '@scoped/react@0.11.0'); -testDependency('@scoped/react', '<0.12', '@scoped/react', '@scoped/react@0.11.0'); -testDependency('@scoped/react', '<0.12.0', '@scoped/react', '@scoped/react@0.11.0'); +testDependency('@scoped/react', '<0.12', '@scoped/react', '@scoped/react@0.11'); +testDependency('@scoped/react', '<0.12.0', '@scoped/react', '@scoped/react@0.11'); testDependency('@scoped/react', '~0.1.1', '@scoped/react', '@scoped/react@~0.1.1'); testDependency('@scoped/react', '^0.12', '@scoped/react', '@scoped/react@^0.12'); -testDependency('@scoped/react', '0.12.x', '@scoped/react', '@scoped/react@~0.12.0'); +testDependency('@scoped/react', '0.12.x', '@scoped/react', '@scoped/react@0.12'); testDependency('@scoped/react', '0.x', '@scoped/react', '@scoped/react@0'); testDependency('@scoped/react', '>=0.12.0', '@scoped/react', '@scoped/react@*'); -testDependency('get-size', '>=1.1.4 <1.3', 'get-size', 'get-size@~1.2.0'); +testDependency('get-size', '>=1.1.4 <1.3', 'get-size', 'get-size@1.2'); + + + console.log('Unit tests passed');