From 8ba72492449f8b98378a2acc3287b02c433a81fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kat=20March=C3=A1n?= Date: Sat, 4 Mar 2017 21:59:29 -0800 Subject: [PATCH] feat(manifest): switch to cacache for caching Fixes: #51 BREAKING CHANGE: Toplevel APIs now return Promises instead of using callbacks. --- README.md | 58 ++++-- extract.js | 50 +++-- lib/cache/index.js | 155 +------------- lib/finalize-manifest.js | 88 ++++---- lib/registry/manifest.js | 38 ++-- lib/registry/pick-manifest.js | 99 ++++----- lib/registry/request.js | 46 ++--- lib/registry/tarball.js | 5 +- manifest.js | 32 +-- prefetch.js | 40 ++-- test/cache.js | 147 -------------- test/extract-stream.chown.js | 57 +++--- test/extract-stream.js | 289 +++++++++++++-------------- test/finalize-manifest.js | 121 +++++------ test/registry.manifest.cache.js | 126 ++++++------ test/registry.manifest.js | 216 +++++++++----------- test/registry.manifest.shrinkwrap.js | 30 ++- test/registry.pick-manifest.js | 198 +++++++++--------- test/registry.tarball.js | 51 ++--- test/util/mock-tarball.js | 52 ++--- test/util/test-dir.js | 37 ++-- test/util/tnock.js | 2 +- 22 files changed, 804 insertions(+), 1133 deletions(-) delete mode 100644 test/cache.js diff --git a/README.md b/README.md index 64de946..128b9e1 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ needed to reduce excess operations, using [`cacache`](https://npm.im/cacache). * [API](#api) * [`manifest`](#manifest) * [`extract`](#extract) + * [`prefetch`](#prefetch) * [`options`](#options) ### Example @@ -27,27 +28,24 @@ needed to reduce excess operations, using [`cacache`](https://npm.im/cacache). ```javascript const pacote = require('pacote') -pacote.manifest('pacote@^1', function (err, pkg) { +pacote.manifest('pacote@^1').then(pkg => { console.log('package manifest for registry pkg:', pkg) // { "name": "pacote", "version": "1.0.0", ... } }) -pacote.extract('http://hi.com/pkg.tgz', './here', function (err) { +pacote.extract('http://hi.com/pkg.tgz', './here').then(() => { console.log('remote tarball contents extracted to ./here') }) ``` ### Features -* simple interface to common package-related actions. -* supports all package types npm does. -* fast cache +* Handles all package types [npm](https://npm.im/npm) does +* [high-performance, reliably, verified local cache](https://npm.im/cacache) * offline mode * authentication support (private git, private npm registries, etc) -* npm-compatible for all relevant operations * github, gitlab, and bitbucket-aware -* version/tag aware when fetching from git repositories. -* caches git repositories +* semver range support for git dependencies ### Contributing @@ -55,9 +53,28 @@ The pacote team enthusiastically welcomes contributions and project participatio ### API -#### `> pacote.manifest(spec, [opts], cb)` +#### `> pacote.manifest(spec, [opts])` -Fetches the *manifest* for a package, aka `package.json`. +Fetches the *manifest* for a package. Manifest objects are similar and based +on the `package.json` for that package, but with pre-processed and limited +fields. The object has the following shape: + +```javascript +{ + "name": PkgName, + "version": SemverString, + "dependencies": { PkgName: SemverString }, + "optionalDependencies": { PkgName: SemverString }, + "devDependencies": { PkgName: SemverString }, + "peerDependencies": { PkgName: SemverString }, + "bundleDependencies": false || [PkgName], + "bin": { BinName: Path }, + "_resolved": TarballSource, // different for each package type + "_shasum": TarballSha1Sum, + "_sha512sum": TarballSha512Sum, + "_shrinkwrap": null || ShrinkwrapJsonObj +} +``` Note that depending on the spec type, some additional fields might be present. For example, packages from `registry.npmjs.org` have additional metadata @@ -66,12 +83,12 @@ appended by the registry. ##### Example ```javascript -pacote.manifest('pacote@1.0.0', function (err, pkgJson) { - // fetched `package.json` data from the registry (or cache, if cached) +pacote.manifest('pacote@1.0.0').then(pkgJson => { + // fetched `package.json` data from the registry }) ``` -#### `> pacote.extract(spec, destination, [opts], cb)` +#### `> pacote.extract(spec, destination, [opts])` Extracts package data identified by `` into a directory named ``, which will be created if it does not already exist. @@ -85,12 +102,25 @@ tarball. ```javascript pacote.extract('pacote@1.0.0', './woot', { digest: 'deadbeef' -}, function (err) { +}).then(() => { // Succeeds as long as `pacote@1.0.0` still exists somewhere. Network and // other operations are bypassed entirely if `digest` is present in the cache. }) ``` +#### `> pacote.prefetch(spec, [opts])` + +Fetches package data identified by ``, usually for the purpose of warming +up the local package cache (with `opts.cache`). It does not return anything. + +##### Example + +```javascript +pacote.prefetch('pacote@1.0.0', { cache: './my-cache' }).then(() => { + // ./my-cache now has both the manifest and tarball for `pacote@1.0.0`. +}) +``` + #### `> options` ##### `opts.digest` diff --git a/extract.js b/extract.js index c37a590..45e10a6 100644 --- a/extract.js +++ b/extract.js @@ -1,48 +1,44 @@ 'use strict' -var cache = require('./lib/cache') -var extractStream = require('./lib/extract-stream') -var pipe = require('mississippi').pipe -var optCheck = require('./lib/util/opt-check') -var rps = require('realize-package-specifier') +const BB = require('bluebird') + +const cache = require('./lib/cache') +const extractStream = require('./lib/extract-stream') +const pipe = BB.promisify(require('mississippi').pipe) +const optCheck = require('./lib/util/opt-check') +const rps = BB.promisify(require('realize-package-specifier')) module.exports = extract -function extract (spec, dest, opts, cb) { - if (!cb) { - cb = opts - opts = null - } +function extract (spec, dest, opts) { opts = optCheck(opts) if (opts.digest) { opts.log.silly('extract', 'trying ', spec, ' digest:', opts.digest) - extractByDigest(dest, opts, function (err) { + return extractByDigest( + dest, opts + ).catch(err => { if (err && err.code === 'ENOENT') { opts.log.silly('extract', 'digest for', spec, 'not present. Using manifest.') - return extractByManifest(spec, dest, opts, cb) + return extractByManifest(spec, dest, opts) } else { - return cb(err) + throw err } }) } else { opts.log.silly('extract', 'no digest provided for ', spec, '- extracting by manifest') - extractByManifest(spec, dest, opts, cb) + return extractByManifest(spec, dest, opts) } } -function extractByDigest (dest, opts, cb) { - var xtractor = extractStream(dest, opts) - var cached = cache.get.stream.byDigest(opts.cache, opts.digest, opts) - pipe(cached, xtractor, cb) +function extractByDigest (dest, opts) { + const xtractor = extractStream(dest, opts) + const cached = cache.get.stream.byDigest(opts.cache, opts.digest, opts) + return pipe(cached, xtractor) } -function extractByManifest (spec, dest, opts, cb) { - var xtractor = extractStream(dest, opts) - rps(spec, function (err, res) { - if (err) { return cb(err) } - var tarball = require('./lib/handlers/' + res.type + '/tarball') - pipe(tarball(res, opts), xtractor, function (err) { - opts.log.silly('extract', 'extraction finished for', spec) - cb(err) - }) +function extractByManifest (spec, dest, opts) { + const xtractor = extractStream(dest, opts) + return rps(spec).then(res => { + const tarball = require('./lib/handlers/' + res.type + '/tarball') + return pipe(tarball(res, opts), xtractor) }) } diff --git a/lib/cache/index.js b/lib/cache/index.js index c50aae0..38e55c8 100644 --- a/lib/cache/index.js +++ b/lib/cache/index.js @@ -2,159 +2,6 @@ var cacache = require('cacache') var cacheKey = require('./cache-key') -var finished = require('mississippi').finished -var through = require('mississippi').through -var pipe = require('mississippi').pipe -var pipeline = require('mississippi').pipeline - -var HASH = 'sha1' +module.exports = cacache module.exports.key = cacheKey - -var MEMOIZED = {} - -module.exports._clearMemoized = clearMemoized -function clearMemoized () { - var old = MEMOIZED - MEMOIZED = {} - return old -} - -module.exports.memoize = memoize -function memoize (key, data) { - MEMOIZED[key] = data -} - -module.exports.readMemoized = readMemoized -function readMemoized (key) { - return MEMOIZED[key] -} - -module.exports.put = putData -function putData (cache, key, data, opts, cb) { - if (!cb) { - cb = opts - opts = null - } - opts = opts || {} - var put = putStream(cache, key, opts) - var dummy = through() - pipe(dummy, put, function (err) { - cb(err) - }) - dummy.write(data) - dummy.end() -} - -module.exports.put.stream = putStream -function putStream (cache, key, opts) { - // This is the most ridiculous thing, but cacache - // needs an API overhaul, and I need to think longer - // about whether to emit the index entry... - var dest = cacache.put.stream(cache, key, opts) - var lol = through() - var piped = pipeline(lol, dest) - var meta - dest.on('metadata', function (m) { - meta = m.metadata - piped.emit('metadata', m.metadata) - }) - dest.on('digest', function (d) { - piped.emit('digest', d) - }) - dest.on('end', function () { - opts.log.silly('cache.put', 'finished writing cache data for', key) - }) - if (opts.memoize) { - opts.log.silly('cache.put', 'memoization of', key, 'requested') - var data = '' - lol.on('data', function (d) { - data += d - }) - finished(piped, function (err) { - if (err) { return } - opts.log.silly('cache.put', key, 'memoized') - MEMOIZED[cache + ':' + key] = { - data: data, - meta: meta || null - } - }) - } - return piped -} - -module.exports.get = getData -function getData (cache, key, opts, cb) { - if (!cb) { - cb = opts - opts = null - } - opts = opts || {} - var meta - var data = '' - var stream = getStream( - false, cache, key, opts - ).on('metadata', function (m) { - meta = m - }).on('data', function (d) { - data += d - }) - finished(stream, function (err) { - cb(err, data, meta) - }) -} - -module.exports.get.stream = function (cache, key, opts) { - return getStream(false, cache, key, opts) -} -module.exports.get.stream.byDigest = function (cache, digest, opts) { - return getStream(true, cache, digest, opts) -} -function getStream (byDigest, cache, key, opts) { - opts = opts || {} - var stream - if (!cache || !key) { - throw new Error('cache and key are required') - } - var memoed = MEMOIZED[cache + ':' + key] - if (memoed) { - opts.log && opts.log.silly('cache.get', key, 'already memoized.') - stream = through() - stream.on('newListener', function (ev, cb) { - ev === 'metadata' && cb(memoed.meta) - }) - stream.write(memoed.data) - stream.end() - return stream - } else { - var dest = through() - var meta - var getter = byDigest ? cacache.get.stream.byDigest : cacache.get.stream - var src = getter(cache, key, { - hashAlgorithm: HASH - }).on('metadata', function (m) { - meta = m.metadata - piped.emit('metadata', m.metadata) - }).on('digest', function (d) { - piped.emit('digest', d) - }) - var piped = pipeline(src, dest) - if (opts.memoize) { - var data = '' - piped.on('data', function (d) { - data += d - }) - finished(piped, function (err) { - if (err) { return } - MEMOIZED[cache + ':' + key] = { - data: data, - meta: meta || null - } - }) - } - return piped - } -} - -module.exports.get.info = cacache.get.info -module.exports.get.hasContent = require('cacache/lib/content/read').hasContent diff --git a/lib/finalize-manifest.js b/lib/finalize-manifest.js index 487feb4..9cba6a7 100644 --- a/lib/finalize-manifest.js +++ b/lib/finalize-manifest.js @@ -1,25 +1,29 @@ 'use strict' -var checksumStream = require('checksum-stream') -var dezalgo = require('dezalgo') -var finished = require('mississippi').finished -var gunzip = require('./util/gunzip-maybe') -var minimatch = require('minimatch') -var normalize = require('normalize-package-data') -var optCheck = require('./util/opt-check') -var path = require('path') -var pipe = require('mississippi').pipe -var pipeline = require('mississippi').pipeline -var tar = require('tar-stream') -var through = require('mississippi').through +const BB = require('bluebird') + +const checksumStream = require('checksum-stream') +const dezalgo = require('dezalgo') +const finished = require('mississippi').finished +const gunzip = require('./util/gunzip-maybe') +const minimatch = require('minimatch') +const normalize = require('normalize-package-data') +const optCheck = require('./util/opt-check') +const path = require('path') +const pipe = require('mississippi').pipe +const pipeline = require('mississippi').pipeline +const tar = require('tar-stream') +const through = require('mississippi').through module.exports = finalizeManifest -function finalizeManifest (pkg, spec, opts, cb) { - tarballedProps(pkg, spec, opts, function (err, props) { - if (err) { return cb(err) } - // normalize should not add any fields, and once - // makeManifest completes, it should never be modified. - cb(null, new Manifest(pkg, props)) +function finalizeManifest (pkg, spec, opts) { + return BB.fromNode(cb => { + tarballedProps(pkg, spec, opts, (err, props) => { + if (err) { return cb(err) } + // normalize should not add any fields, and once + // makeManifest completes, it should never be modified. + cb(null, new Manifest(pkg, props)) + }) }) } @@ -31,7 +35,7 @@ function Manifest (pkg, fromTarball) { this.dependencies = pkg.dependencies || {} this.optionalDependencies = pkg.optionalDependencies || {} this.devDependencies = pkg.devDependencies || {} - var bundled = ( + const bundled = ( pkg.bundledDependencies || pkg.bundleDependencies || false @@ -51,10 +55,10 @@ function Manifest (pkg, fromTarball) { if (this.bin && Array.isArray(this.bin)) { // Code yanked from read-package-json. - var m = pkg.directories && pkg.directories.bin || '.' - this.bin = this.bin.reduce(function (acc, mf) { + const m = pkg.directories && pkg.directories.bin || '.' + this.bin = this.bin.reduce((acc, mf) => { if (mf && mf.charAt(0) !== '.') { - var f = path.basename(mf) + const f = path.basename(mf) acc[f] = path.join(m, mf) } return acc @@ -79,41 +83,41 @@ function Manifest (pkg, fromTarball) { // from the stream. function tarballedProps (pkg, spec, opts, cb) { cb = dezalgo(cb) - var extraProps = {} - var needsShrinkwrap = ( + const extraProps = {} + const needsShrinkwrap = ( pkg._hasShrinkwrap !== false && !pkg._shrinkwrap ) - var needsBin = !!( + const needsBin = !!( !pkg.bin && pkg.directories && pkg.directories.bin ) - var needsShasum = !pkg._shasum + const needsShasum = !pkg._shasum if (!needsShrinkwrap && !needsBin && !needsShasum) { opts.log.silly('finalize-manifest', 'Skipping tarball extraction -- nothing needed.') return cb(null, extraProps) } else { opts = optCheck(opts) opts.memoize = false - var tarball = require('./handlers/' + spec.type + '/tarball') - var tarData = tarball.fromManifest(pkg, spec, opts) - var shaStream = null - var extractorStream = null + const tarball = require('./handlers/' + spec.type + '/tarball') + const tarData = tarball.fromManifest(pkg, spec, opts) + let shaStream = null + let extractorStream = null if (needsShrinkwrap || needsBin) { opts.log.silly('finalize-manifest', 'parsing tarball for', spec.name) - var dirBin = pkg.directories && pkg.directories.bin + const dirBin = pkg.directories && pkg.directories.bin extraProps.bin = pkg.bin || {} - var dataStream = tar.extract() + const dataStream = tar.extract() extractorStream = pipeline(gunzip(), dataStream) - dataStream.on('entry', function doEntry (header, fileStream, next) { - var filePath = header.name.replace(/[^/]+\//, '') + dataStream.on('entry', (header, fileStream, next) => { + const filePath = header.name.replace(/[^/]+\//, '') if (needsShrinkwrap && filePath === 'npm-shrinkwrap.json') { - var srData = '' - fileStream.on('data', function (d) { srData += d }) + let srData = '' + fileStream.on('data', d => { srData += d }) - return finished(fileStream, function (err) { + return finished(fileStream, err => { if (err) { return dataStream.emit('error', err) } try { extraProps._shrinkwrap = JSON.parse(srData) @@ -123,13 +127,13 @@ function tarballedProps (pkg, spec, opts, cb) { } }) } else if (needsBin && minimatch(filePath, dirBin + '/**')) { - var relative = path.relative(dirBin, filePath) + const relative = path.relative(dirBin, filePath) if (relative && relative[0] !== '.') { extraProps.bin[path.basename(relative)] = path.join(dirBin, relative) } } // Drain and get next one - fileStream.on('data', function () {}) + fileStream.on('data', () => {}) next() }) } else { @@ -139,15 +143,15 @@ function tarballedProps (pkg, spec, opts, cb) { shaStream = checksumStream({ algorithm: opts.hashAlgorithm }) - shaStream.on('digest', function (d) { + shaStream.on('digest', d => { extraProps._shasum = d }) } else { shaStream = through() } // Drain the end stream - extractorStream.on('data', function () {}) - return pipe(tarData, shaStream, extractorStream, function (err) { + extractorStream.on('data', () => {}) + return pipe(tarData, shaStream, extractorStream, err => { cb(err, extraProps) }) } diff --git a/lib/registry/manifest.js b/lib/registry/manifest.js index 94eb484..7db4b16 100644 --- a/lib/registry/manifest.js +++ b/lib/registry/manifest.js @@ -18,30 +18,28 @@ function manifest (spec, opts, cb) { 'registry.manifest', 'looking up registry-based metadata for ', spec ) - request(uri, registry, opts, function (err, metadata) { - if (err) { return cb(err) } + return request(uri, registry, opts).then(metadata => { opts.log.silly('registry.manifest', 'got metadata for', spec.name) - pickManifest(metadata, spec, { + return pickManifest(metadata, spec, { engineFilter: opts.engineFilter, defaultTag: opts.defaultTag - }, function (err, manifest) { - if (err) { return cb(err) } - // Done here instead of ./finalize-manifest because these fields - // have to be filled in differently depending on type. - manifest._shasum = ( - manifest.dist && manifest.dist.shasum - ) || manifest._shasum - manifest._resolved = ( - manifest.dist && manifest.dist.tarball - ) || url.resolve( - registry, - '/' + manifest.name + - '/-/' + manifest.name + - '-' + manifest.version + - '.tgz' - ) - cb(null, manifest) }) + }).then(manifest => { + // Done here instead of ./finalize-manifest because these fields + // have to be filled in differently depending on type. + manifest._shasum = ( + manifest.dist && manifest.dist.shasum + ) || manifest._shasum + manifest._resolved = ( + manifest.dist && manifest.dist.tarball + ) || url.resolve( + registry, + '/' + manifest.name + + '/-/' + manifest.name + + '-' + manifest.version + + '.tgz' + ) + return manifest }) } diff --git a/lib/registry/pick-manifest.js b/lib/registry/pick-manifest.js index 8116707..2dc5af8 100644 --- a/lib/registry/pick-manifest.js +++ b/lib/registry/pick-manifest.js @@ -1,66 +1,71 @@ 'use strict' +var BB = require('bluebird') + var semver = require('semver') module.exports = pickManifest -function pickManifest (metadata, spec, opts, cb) { +function pickManifest (metadata, spec, opts) { var distTags = metadata['dist-tags'] || {} var versions = Object.keys(metadata.versions || {}) versions = versions.filter(function (v) { return semver.valid(v) }) var err + return BB.fromNode(cb => { + if (!versions.length) { + err = new Error('Package has no valid versions.') + err.code = 'ENOVERSIONS' + err.name = metadata.name + err.spec = spec + return cb(err) + } - if (!versions.length) { - err = new Error('Package has no valid versions.') - err.code = 'ENOVERSIONS' - err.name = metadata.name - err.spec = spec - return cb(err) - } - - var target + var target - if (spec.type === 'tag') { - target = distTags[spec.spec] - } else if (spec.type === 'version') { - target = spec.spec - } else if (spec.type !== 'range') { - return cb(new Error('Only tag, version, and range are supported')) - } + if (spec.type === 'tag') { + target = distTags[spec.spec] + } else if (spec.type === 'version') { + target = spec.spec + } else if (spec.type !== 'range') { + return cb(new Error('Only tag, version, and range are supported')) + } - var tagVersion = distTags[opts.defaultTag || 'latest'] + var tagVersion = distTags[opts.defaultTag || 'latest'] - if (!target && + if ( + !target && tagVersion && metadata.versions[tagVersion] && - semver.satisfies(tagVersion, spec.spec, true)) { - target = tagVersion - } + semver.satisfies(tagVersion, spec.spec, true) + ) { + target = tagVersion + } - if (!target) { - target = semver.maxSatisfying(versions, spec.spec, true) - } + if (!target) { + target = semver.maxSatisfying(versions, spec.spec, true) + } - if (!target && spec.spec === '*') { - // npm hard-codes `latest` here, but it's likely intended - // to be `defaultTag`. - // - // This specific corner is meant for the case where - // someone is using `*` as a selector, but all versions - // are pre-releases, which don't match ranges at all. - target = tagVersion - } + if (!target && spec.spec === '*') { + // npm hard-codes `latest` here, but it's likely intended + // to be `defaultTag`. + // + // This specific corner is meant for the case where + // someone is using `*` as a selector, but all versions + // are pre-releases, which don't match ranges at all. + target = tagVersion + } - var manifest = target && metadata.versions[target] - if (!manifest) { - err = new Error('No matching versions') - err.code = 'ENOENT' - err.name = metadata.name - err.spec = spec - err.versions = versions - err.distTags = distTags - err.defaultTag = opts.defaultTag - return cb(err) - } else { - return cb(null, manifest) - } + var manifest = target && metadata.versions[target] + if (!manifest) { + err = new Error('No matching versions') + err.code = 'ENOENT' + err.name = metadata.name + err.spec = spec + err.versions = versions + err.distTags = distTags + err.defaultTag = opts.defaultTag + return cb(err) + } else { + return cb(null, manifest) + } + }) } diff --git a/lib/registry/request.js b/lib/registry/request.js index 8b15df9..9553c76 100644 --- a/lib/registry/request.js +++ b/lib/registry/request.js @@ -1,8 +1,10 @@ 'use strict' +var BB = require('bluebird') + var cache = require('../cache') var finished = require('mississippi').finished -var inflight = require('inflight') +var pinflight = require('promise-inflight') var gunzip = require('../util/gunzip-maybe') var pipe = require('mississippi').pipe var registryKey = require('./registry-key') @@ -10,34 +12,24 @@ var through = require('mississippi').through var to = require('mississippi').to module.exports = get -function get (uri, registry, opts, cb) { +function get (uri, registry, opts) { var key = cache.key('registry-request', uri) - var memoed = cache.readMemoized(key) - if (memoed) { - opts.log.silly('registry.get', key, 'already memoized!') - return cb(null, memoed.data, memoed.raw) - } - cb = inflight(key, cb) - if (!cb) { - opts.log.silly('registry.get', key, 'is already inflight') - return - } - var raw = '' - var stream = getStream(uri, registry, opts) - stream.on('data', function (d) { raw += d }) - stream.on('reset', function () { raw = '' }) - finished(stream, function (err) { - if (err) { return cb(err) } - try { - var parsed = JSON.parse(raw) - } catch (e) { - return cb(e) - } - cache.memoize(key, { - data: parsed, - raw: raw + return pinflight(key, () => { + return BB.fromNode(cb => { + var raw = '' + var stream = getStream(uri, registry, opts) + stream.on('data', function (d) { raw += d }) + stream.on('reset', function () { raw = '' }) + finished(stream, function (err) { + if (err) { return cb(err) } + try { + var parsed = JSON.parse(raw) + } catch (e) { + return cb(e) + } + return cb(null, parsed) + }) }) - return cb(null, parsed, raw) }) } diff --git a/lib/registry/tarball.js b/lib/registry/tarball.js index 633ba44..8437b4b 100644 --- a/lib/registry/tarball.js +++ b/lib/registry/tarball.js @@ -15,8 +15,7 @@ function tarball (spec, opts) { 'looking up registry-based metadata for ', spec ) var stream = through() - manifest(spec, opts, function (err, manifest) { - if (err) { return stream.emit('error', err) } + manifest(spec, opts).then(manifest => { opts.log.silly( 'registry.tarball', 'registry metadata found. Downloading ', manifest.name + '@' + manifest.version @@ -25,7 +24,7 @@ function tarball (spec, opts) { fromManifest(manifest, spec, opts), stream ) - }) + }, err => stream.emit('error', err)) return stream } diff --git a/manifest.js b/manifest.js index e46a2dd..e238d07 100644 --- a/manifest.js +++ b/manifest.js @@ -1,25 +1,27 @@ 'use strict' -var finalizeManifest = require('./lib/finalize-manifest') -var optCheck = require('./lib/util/opt-check') -var rps = require('realize-package-specifier') +const BB = require('bluebird') -var handlers = {} +const finalizeManifest = require('./lib/finalize-manifest') +const optCheck = require('./lib/util/opt-check') +const rps = BB.promisify(require('realize-package-specifier')) + +let handlers = {} module.exports = manifest -function manifest (spec, opts, cb) { - if (!cb) { - cb = opts - opts = null - } +function manifest (spec, opts) { opts = optCheck(opts) - rps(spec, function (err, res) { - if (err) { return cb(err) } - var fetcher = handlers[res.type] || (handlers[res.type] = require('./lib/handlers/' + res.type + '/manifest')) - fetcher(res, opts, function (err, mani) { - if (err) { return cb(err) } - finalizeManifest(mani, res, opts, cb) + return rps(spec).then(res => { + const fetcher = ( + handlers[res.type] || + ( + handlers[res.type] = + require('./lib/handlers/' + res.type + '/manifest') + ) + ) + return fetcher(res, opts).then(manifest => { + return finalizeManifest(manifest, res, opts) }) }) } diff --git a/prefetch.js b/prefetch.js index ebb586c..091fb51 100644 --- a/prefetch.js +++ b/prefetch.js @@ -1,46 +1,38 @@ 'use strict' -var cache = require('./lib/cache') -var finished = require('mississippi').finished -var optCheck = require('./lib/util/opt-check') -var rps = require('realize-package-specifier') +const BB = require('bluebird') + +const cache = require('./lib/cache') +const finished = BB.promisify(require('mississippi').finished) +const optCheck = require('./lib/util/opt-check') +const rps = BB.promisify(require('realize-package-specifier')) module.exports = prefetch -function prefetch (spec, opts, cb) { - if (!cb) { - cb = opts - opts = null - } +function prefetch (spec, opts) { opts = optCheck(opts) if (!opts.cache) { opts.log.info('prefetch', 'skipping prefetch: no cache provided') - setImmediate(function () { cb() }) + return BB.resolve() } if (opts.digest) { opts.log.silly('prefetch', 'checking if ', spec, ' digest is already cached') - cache.get.hasContent(opts.cache, opts.digest, function (err, exists) { - if (err) { return cb(err) } + return cache.get.hasContent(opts.cache, opts.digest).then(exists => { if (exists) { opts.log.silly('prefetch', 'content already exists for', spec) - return cb(null) } else { - return prefetchByManifest(spec, opts, cb) + return prefetchByManifest(spec, opts) } }) } else { opts.log.silly('prefetch', 'no digest provided for ', spec, '- fetching by manifest') - prefetchByManifest(spec, opts, cb) + return prefetchByManifest(spec, opts) } } -function prefetchByManifest (spec, opts, cb) { - rps(spec, function (err, res) { - if (err) { return cb(err) } - var stream = require('./lib/handlers/' + res.type + '/tarball')(res, opts) - finished(stream, function (err) { - opts.log.silly('prefetch', 'prefetch finished for', spec) - cb(err) - }) - stream.on('data', function () {}) +function prefetchByManifest (spec, opts) { + return rps(spec).then(res => { + const stream = require('./lib/handlers/' + res.type + '/tarball')(res, opts) + setImmediate(() => stream.on('data', function () {})) + return finished(stream) }) } diff --git a/test/cache.js b/test/cache.js deleted file mode 100644 index d77c32e..0000000 --- a/test/cache.js +++ /dev/null @@ -1,147 +0,0 @@ -'use strict' - -var crypto = require('crypto') -var finished = require('mississippi').finished -var fromString = require('./util/from-string') -var npmlog = require('npmlog') -var path = require('path') -var pipe = require('mississippi').pipe -var rimraf = require('rimraf') -var test = require('tap').test -var testDir = require('./util/test-dir') - -var CACHE = testDir(__filename) -var cache = require('../lib/cache') -var cacache = require('cacache') -var CONTENTS = 'foobarbaz' -var DIGEST = crypto.createHash('sha1').update(CONTENTS).digest('hex') -var KEY = 'testkey' -var OPTS = { - log: npmlog, - memoize: true, - metadata: { - foo: 'bar' - } -} - -test('cache key generation', function (t) { - var key = cache.key('abc', 'def') - t.equal(key, 'pacote:abc:def', 'useful unique key gen') - t.done() -}) - -test('streaming cache get', function (t) { - cache._clearMemoized() - cacache.put(CACHE, KEY, CONTENTS, OPTS, function (err) { - if (err) { throw err } - t.comment('mock cache contents inserted') - var contents = '' - var meta - var stream = cache.get.stream( - CACHE, KEY, OPTS - ).on('data', function (d) { - contents += d - }).on('metadata', function (m) { - meta = m - }) - finished(stream, function (err) { - if (err) { throw err } - t.equal(contents, CONTENTS, 'stream extracted cache contents correctly') - t.deepEqual(meta, OPTS.metadata, 'metadata contents correct') - t.done() - }) - }) -}) - -test('bulk cache get', function (t) { - cache._clearMemoized() - cacache.put(CACHE, KEY, CONTENTS, OPTS, function (err) { - if (err) { throw err } - t.comment('mock cache contents inserted') - cache.get(CACHE, KEY, OPTS, function (err, data, meta) { - if (err) { throw err } - t.equal(data, CONTENTS, 'contents returned correctly') - t.deepEqual(meta, OPTS.metadata, 'returned metadata') - var loc = path.join(CACHE, 'content', DIGEST) - rimraf(loc, function (err) { - if (err) { throw err } - t.comment('blew away cache from disk') - cache.get(CACHE, KEY, OPTS, function (err, d2, m2) { - if (err) { throw err } - t.deepEqual({ - data: d2, - meta: m2 - }, { - data: data, - meta: meta - }, 'contents memoized') - t.done() - }) - }) - }) - }) -}) - -test('streaming cache put', function (t) { - cache._clearMemoized() - var dig - var src = fromString(CONTENTS) - var dest = cache.put.stream( - CACHE, KEY, OPTS - ).on('digest', function (d) { dig = d }) - pipe(src, dest, function (err) { - if (err) { throw err } - t.equal(dig, DIGEST, 'emitted correct digest') - cacache.get(CACHE, KEY, OPTS, function (err, data, meta) { - if (err) { throw err } - var memoized = cache._clearMemoized()[CACHE + ':' + KEY] - t.equal(data, CONTENTS, 'data in db correct') - t.deepEqual(meta.metadata, OPTS.metadata, 'got metadata') - t.deepEqual(memoized, { - data: CONTENTS, - meta: OPTS.metadata - }, 'data memoized') - t.done() - }) - }) -}) - -test('bulk cache put', function (t) { - cache._clearMemoized() - cache.put(CACHE, KEY, CONTENTS, OPTS, function (err) { - if (err) { throw err } - cacache.get(CACHE, KEY, OPTS, function (err, data, meta) { - if (err) { throw err } - var memoized = cache._clearMemoized()[CACHE + ':' + KEY] - t.equal(data, CONTENTS, 'data in db correct') - t.ok(meta, 'got metadata') - delete meta.time - var target = { - path: path.join(CACHE, 'content', DIGEST), - key: KEY, - digest: DIGEST, - metadata: OPTS.metadata - } - t.deepEqual(meta, target, 'cache index entry correct') - t.deepEqual(memoized, { - data: data, - meta: OPTS.metadata - }, 'contents memoized') - t.done() - }) - }) -}) - -test('opts are optional', function (t) { - cache._clearMemoized() - cache.put(CACHE, KEY, CONTENTS, function (err) { - if (err) { throw err } - t.ok(true, 'put succeeded') - cache.get(CACHE, KEY, function (err, data, meta) { - if (err) { throw err } - t.equal(data, CONTENTS, 'got data!') - t.notOk(meta, 'metadata empty!') - t.done() - }) - }) -}) diff --git a/test/extract-stream.chown.js b/test/extract-stream.chown.js index 6f87ecb..4d7f355 100644 --- a/test/extract-stream.chown.js +++ b/test/extract-stream.chown.js @@ -1,12 +1,14 @@ 'use strict' -var fs = require('fs') -var mockTar = require('./util/mock-tarball') -var npmlog = require('npmlog') -var path = require('path') -var pipe = require('mississippi').pipe -var requireInject = require('require-inject') -var test = require('tap').test +const BB = require('bluebird') + +const fs = require('fs') +const mockTar = require('./util/mock-tarball') +const npmlog = require('npmlog') +const path = require('path') +const pipe = BB.promisify(require('mississippi').pipe) +const requireInject = require('require-inject') +const test = require('tap').test require('./util/test-dir')(__filename) @@ -15,7 +17,7 @@ npmlog.level = process.env.LOGLEVEL || 'silent' test('accepts gid and uid opts', { skip: !process.getuid }, function (t) { - var pkg = { + const pkg = { 'package.json': { data: JSON.stringify({ name: 'foo', @@ -24,14 +26,14 @@ test('accepts gid and uid opts', { }, 'foo/index.js': 'console.log("hello world!")' } - var NEWUID = process.getuid() + 1 - var NEWGID = process.getgid() + 1 + const NEWUID = process.getuid() + 1 + const NEWGID = process.getgid() + 1 // All of this only happens on uid === 0 - process.getuid = function () { return 0 } - var updatedPaths = [] - var fsClone = Object.create(fs) - fsClone.chown = function (p, uid, gid, cb) { - process.nextTick(function () { + process.getuid = () => 0 + const updatedPaths = [] + const fsClone = Object.create(fs) + fsClone.chown = (p, uid, gid, cb) => { + process.nextTick(() => { t.deepEqual({ uid: uid, gid: gid @@ -43,24 +45,21 @@ test('accepts gid and uid opts', { cb(null) }) } - var extractStream = requireInject('../lib/extract-stream', { + const extractStream = requireInject('../lib/extract-stream', { fs: fsClone }) - mockTar(pkg, {stream: true}, function (err, tarStream) { - if (err) { throw err } - pipe(tarStream, extractStream('./target', { + return mockTar(pkg, {stream: true}).then(tarStream => { + return pipe(tarStream, extractStream('./target', { uid: NEWUID, gid: NEWGID, log: npmlog - }), function (err) { - if (err) { throw err } - t.deepEqual(updatedPaths, [ - 'target', - 'target/package.json', - 'target/foo', - 'target/foo/index.js' - ], 'extracted files had correct uid/gid set') - t.done() - }) + })) + }).then(() => { + t.deepEqual(updatedPaths, [ + 'target', + 'target/package.json', + 'target/foo', + 'target/foo/index.js' + ], 'extracted files had correct uid/gid set') }) }) diff --git a/test/extract-stream.js b/test/extract-stream.js index 24f6219..bff1725 100644 --- a/test/extract-stream.js +++ b/test/extract-stream.js @@ -1,22 +1,24 @@ 'use strict' -var fs = require('fs') -var mockTar = require('./util/mock-tarball') -var npmlog = require('npmlog') -var pipe = require('mississippi').pipe -var test = require('tap').test +const BB = require('bluebird') + +const fs = BB.promisifyAll(require('fs')) +const mockTar = require('./util/mock-tarball') +const npmlog = require('npmlog') +const pipe = BB.promisify(require('mississippi').pipe) +const test = require('tap').test require('./util/test-dir')(__filename) -var extractStream = require('../lib/extract-stream') +const extractStream = require('../lib/extract-stream') npmlog.level = process.env.LOGLEVEL || 'silent' -var OPTS = { +const OPTS = { log: npmlog } -test('basic extraction', function (t) { - var pkg = { +test('basic extraction', t => { + const pkg = { 'package.json': JSON.stringify({ name: 'foo', version: '1.0.0' @@ -24,76 +26,66 @@ test('basic extraction', function (t) { 'index.js': 'console.log("hello world!")' } t.plan(2) - mockTar(pkg, {stream: true}, function (err, tarStream) { - if (err) { throw err } - pipe(tarStream, extractStream('./'), function (err) { - if (err) { throw err } - fs.readFile('./package.json', 'utf8', function (err, data) { - if (err) { throw err } - t.deepEqual(data, pkg['package.json'], 'extracted package.json') - }) - fs.readFile('./index.js', 'utf8', function (err, data) { - if (err) { throw err } - t.equal(data, pkg['index.js'], 'extracted index.js') - }) - }) + return mockTar(pkg, {stream: true}).then(tarStream => { + return pipe(tarStream, extractStream('./')) + }).then(() => { + return fs.readFileAsync('./package.json', 'utf8') + }).then(data => { + t.deepEqual(data, pkg['package.json'], 'extracted package.json') + return fs.readFileAsync('./index.js', 'utf8') + }).then(data => { + t.equal(data, pkg['index.js'], 'extracted index.js') }) }) -test('automatically handles gzipped tarballs', function (t) { - var pkg = { +test('automatically handles gzipped tarballs', t => { + const pkg = { 'package.json': JSON.stringify({ name: 'foo', version: '1.0.0' }), 'index.js': 'console.log("hello world!")' } - t.plan(2) - mockTar(pkg, {gzip: true, stream: true}, function (err, tarStream) { - if (err) { throw err } - pipe(tarStream, extractStream('./', OPTS), function (err) { - if (err) { throw err } - fs.readFile('./package.json', 'utf8', function (err, data) { - if (err) { throw err } - t.deepEqual(data, pkg['package.json'], 'got gunzipped package.json') - }) - fs.readFile('./index.js', 'utf8', function (err, data) { - if (err) { throw err } - t.equal(data, pkg['index.js'], 'got gunzipped index.js') - }) - }) + return mockTar(pkg, {gzip: true, stream: true}).then(tarStream => { + return pipe(tarStream, extractStream('./', OPTS)) + }).then(() => { + return BB.join( + fs.readFileAsync('./package.json', 'utf8'), + fs.readFileAsync('./index.js', 'utf8'), + (json, indexjs) => { + t.deepEqual(json, pkg['package.json'], 'got gunzipped package.json') + t.equal(indexjs, pkg['index.js'], 'got gunzipped index.js') + } + ) }) }) -test('strips first item in path, even if not `package/`', function (t) { - var pkg = { +test('strips first item in path, even if not `package/`', t => { + const pkg = { 'package/package.json': JSON.stringify({ name: 'foo', version: '1.0.0' }), 'something-else/index.js': 'console.log("hello world!")' } - t.plan(2) - mockTar(pkg, {noPrefix: true, stream: true}, function (err, tarStream) { - if (err) { throw err } - pipe(tarStream, extractStream('./', OPTS), function (err) { - if (err) { throw err } - fs.readFile('./package.json', 'utf8', function (err, data) { - if (err) { throw err } + return mockTar(pkg, {noPrefix: true, stream: true}).then(tarStream => { + return pipe(tarStream, extractStream('./', OPTS)) + }).then(() => { + return BB.join( + fs.readFileAsync('./package.json', 'utf8'), + fs.readFileAsync('./index.js', 'utf8'), + (json, indexjs) => { t.deepEqual( - data, pkg['package/package.json'], 'flattened package.json') - }) - fs.readFile('./index.js', 'utf8', function (err, data) { - if (err) { throw err } + json, pkg['package/package.json'], 'flattened package.json') t.equal( - data, pkg['something-else/index.js'], 'flattened index.js') - }) - }) + indexjs, pkg['something-else/index.js'], 'flattened index.js') + } + ) }) }) -test('excludes symlinks', function (t) { - var pkg = { +test('excludes symlinks', t => { + const pkg = { 'package.json': JSON.stringify({ name: 'foo', version: '1.0.0' @@ -102,97 +94,109 @@ test('excludes symlinks', function (t) { 'linky': { type: 'link', linkname: '/usr/local/bin/linky' }, 'symmylinky': { type: 'symlink', linkname: '../nowhere' } } - t.plan(3) - mockTar(pkg, {stream: true}, function (err, tarStream) { - if (err) { throw err } - pipe(tarStream, extractStream('./', OPTS), function (err) { - if (err) { throw err } - fs.readFile('./package.json', 'utf8', function (err, data) { - if (err) { throw err } + return mockTar(pkg, {stream: true}).then(tarStream => { + return pipe(tarStream, extractStream('./', OPTS)) + }).then(() => { + return BB.join( + fs.readFileAsync('./package.json', 'utf8').then(data => { t.deepEqual(data, pkg['package.json'], 'package.json still there') - }) - fs.stat('./linky', function (err) { - t.equal(err.code, 'ENOENT', 'hard link excluded!') - }) - fs.readFile('./symmylinky', function (err) { - t.equal(err.code, 'ENOENT', 'symlink excluded!') - }) - }) + }), + fs.statAsync('./linky').then( + () => { throw new Error('this was supposed to error') }, + err => { + t.equal(err.code, 'ENOENT', 'hard link excluded!') + } + ), + fs.readFileAsync('./symmylinky').then( + () => { throw new Error('this was supposed to error') }, + err => { + t.equal(err.code, 'ENOENT', 'symlink excluded!') + } + ) + ) }) }) // Yes, this logic is terrible and seriously confusing, but // I'm pretty sure this is exactly what npm is doing. // ...we should really deprecate this cluster. -test('renames .gitignore to .npmignore if not present', function (t) { - t.plan(6) - mockTar({ - 'package.json': JSON.stringify({ - name: 'foo', - version: '1.0.0' - }), - 'index.js': 'console.log("hello world!")', - '.gitignore': 'tada!' - }, {stream: true}, function (err, tarStream) { - if (err) { throw err } - pipe(tarStream, extractStream('./no-npmignore', OPTS), function (err) { - if (err) { throw err } - fs.readFile('./no-npmignore/.npmignore', 'utf8', function (err, data) { - if (err) { throw err } +test('renames .gitignore to .npmignore if not present', t => { + return BB.join( + mockTar({ + 'package.json': JSON.stringify({ + name: 'foo', + version: '1.0.0' + }), + 'index.js': 'console.log("hello world!")', + '.gitignore': 'tada!' + }, {stream: true}).then(tarStream => { + return pipe(tarStream, extractStream('./no-npmignore', OPTS)) + }).then(() => { + return fs.readFileAsync( + './no-npmignore/.npmignore', 'utf8' + ).then(data => { t.deepEqual(data, 'tada!', '.gitignore renamed to .npmignore') }) - }) - }) - mockTar({ - 'package.json': JSON.stringify({ - name: 'foo', - version: '1.0.0' }), - 'index.js': 'console.log("hello world!")', - '.gitignore': 'git!', - '.npmignore': 'npm!' - }, {stream: true}, function (err, tarStream) { - if (err) { throw err } - pipe(tarStream, extractStream('./has-npmignore1', OPTS), function (err) { - if (err) { throw err } - fs.readFile('./has-npmignore1/.npmignore', 'utf8', function (err, data) { - if (err) { throw err } - t.deepEqual(data, 'npm!', '.npmignore left intact if present') - }) - fs.readFile('./has-npmignore1/.gitignore', 'utf8', function (err, data) { - t.ok(err, 'got expected error on reading .gitignore') - t.equal(err.code, 'ENOENT', '.gitignore missing') - }) - }) - }) - mockTar({ - 'package.json': JSON.stringify({ - name: 'foo', - version: '1.0.0' + mockTar({ + 'package.json': JSON.stringify({ + name: 'foo', + version: '1.0.0' + }), + 'index.js': 'console.log("hello world!")', + '.gitignore': 'git!', + '.npmignore': 'npm!' + }, {stream: true}).then(tarStream => { + return pipe(tarStream, extractStream('./has-npmignore1', OPTS)) + }).then(() => { + return BB.join( + fs.readFileAsync( + './has-npmignore1/.npmignore', 'utf8' + ).then(data => { + t.deepEqual(data, 'npm!', '.npmignore left intact if present') + }), + fs.readFileAsync( + './has-npmignore1/.gitignore', 'utf8' + ).then( + () => { throw new Error('expected an error') }, + err => { + t.ok(err, 'got expected error on reading .gitignore') + t.equal(err.code, 'ENOENT', '.gitignore missing') + } + ) + ) }), - 'index.js': 'console.log("hello world!")', - '.npmignore': 'npm!', - '.gitignore': 'git!' - }, {stream: true}, function (err, tarStream) { - if (err) { throw err } - pipe(tarStream, extractStream('./has-npmignore2', OPTS), function (err) { - if (err) { throw err } - fs.readFile('./has-npmignore2/.npmignore', 'utf8', function (err, data) { - if (err) { throw err } - t.deepEqual(data, 'npm!', '.npmignore left intact if present') - }) - fs.readFile('./has-npmignore2/.gitignore', 'utf8', function (err, data) { - if (err) { throw err } - t.deepEqual(data, 'git!', '.gitignore intact if we previously had an .npmignore') - }) + mockTar({ + 'package.json': JSON.stringify({ + name: 'foo', + version: '1.0.0' + }), + 'index.js': 'console.log("hello world!")', + '.npmignore': 'npm!', + '.gitignore': 'git!' + }, {stream: true}).then(tarStream => { + return pipe(tarStream, extractStream('./has-npmignore2', OPTS)) + }).then(() => { + return BB.join( + fs.readFileAsync( + './has-npmignore2/.npmignore', 'utf8' + ).then(data => { + t.deepEqual(data, 'npm!', '.npmignore left intact if present') + }), + fs.readFileAsync( + './has-npmignore2/.gitignore', 'utf8' + ).then(data => { + t.deepEqual(data, 'git!', '.gitignore intact if we previously had an .npmignore') + }) + ) }) - }) + ) }) test('accepts dmode/fmode/umask opts', { skip: process.platform === 'win32' -}, function (t) { - var pkg = { +}, t => { + const pkg = { 'package.json': { data: JSON.stringify({ name: 'foo', @@ -204,41 +208,36 @@ test('accepts dmode/fmode/umask opts', { }, 'foo/index.js': 'console.log("hello world!")' } - t.plan(2) - mockTar(pkg, {stream: true}, function (err, tarStream) { - if (err) { throw err } - pipe(tarStream, extractStream('./', { + return mockTar(pkg, {stream: true}).then(tarStream => { + return pipe(tarStream, extractStream('./', { dmode: parseInt('555', 8), fmode: parseInt('644', 8), umask: parseInt('266', 8) - }), function (err) { - if (err) { throw err } - fs.stat('./package.json', function (err, stat) { - if (err) { throw err } + })) + }).then(() => { + return BB.join( + fs.statAsync('./package.json').then(stat => { t.equal( stat.mode & parseInt('000777', 8), parseInt('400', 8), 'fmode set as expected' ) - }) + }), // TODO - I don't understand why this one is always 755 - // :( // fs.stat('./foo', function (err, stat) { - // if (err) { throw err } // t.equal( // stat.mode & parseInt('000777', 8), // parseInt('411', 8), // 'dmode set as expected' // ) // }) - fs.stat('./foo/index.js', function (err, stat) { - if (err) { throw err } + fs.statAsync('./foo/index.js').then(stat => { t.equal( stat.mode & parseInt('000777', 8), parseInt('400', 8), 'fmode set as expected' ) }) - }) + ) }) }) diff --git a/test/finalize-manifest.js b/test/finalize-manifest.js index d02c129..efb235e 100644 --- a/test/finalize-manifest.js +++ b/test/finalize-manifest.js @@ -1,18 +1,20 @@ 'use strict' -var crypto = require('crypto') -var npmlog = require('npmlog') -var path = require('path') -var tar = require('tar-stream') -var test = require('tap').test -var tnock = require('./util/tnock') +const BB = require('bluebird') + +const crypto = require('crypto') +const npmlog = require('npmlog') +const path = require('path') +const tar = require('tar-stream') +const test = require('tap').test +const tnock = require('./util/tnock') require('./util/test-dir')(__filename) -var finalizeManifest = require('../lib/finalize-manifest') +const finalizeManifest = require('../lib/finalize-manifest') npmlog.level = process.env.LOGLEVEL || 'silent' -var OPTS = { +const OPTS = { registry: 'https://mock.reg/', log: npmlog, retry: { @@ -23,8 +25,8 @@ var OPTS = { } } -test('returns a manifest with the right fields', function (t) { - var base = { +test('returns a manifest with the right fields', t => { + const base = { name: 'testing', version: '1.2.3', dependencies: { x: '3.2.1' }, @@ -37,8 +39,7 @@ test('returns a manifest with the right fields', function (t) { _shasum: 'deadbeefc0ffeebad1dea', _hasShrinkwrap: false } - finalizeManifest(base, {}, OPTS, function (err, manifest) { - if (err) { throw err } + return finalizeManifest(base, {}, OPTS).then(manifest => { t.deepEqual(manifest, { name: 'testing', version: '1.2.3', @@ -55,20 +56,18 @@ test('returns a manifest with the right fields', function (t) { _shrinkwrap: null, _id: 'testing@1.2.3' }, 'fields as expected') - t.end() }) }) -test('defaults all field to expected types + values', function (t) { - var base = { +test('defaults all field to expected types + values', t => { + const base = { name: 'testing', version: '1.2.3', _resolved: 'resolved.to.this', _shasum: 'deadbeefc0ffeebad1dea', _hasShrinkwrap: false } - finalizeManifest(base, {}, OPTS, function (err, manifest) { - if (err) { throw err } + return finalizeManifest(base, {}, OPTS).then(manifest => { t.deepEqual(manifest, { name: base.name, version: base.version, @@ -83,73 +82,66 @@ test('defaults all field to expected types + values', function (t) { _shrinkwrap: null, _id: 'testing@1.2.3' }, 'fields defaulted as expected') - t.end() }) }) -test('fills in shrinkwrap if missing', function (t) { - var tarballPath = 'testing/tarball-1.2.3.tgz' - var base = { +test('fills in shrinkwrap if missing', t => { + const tarballPath = 'testing/tarball-1.2.3.tgz' + const base = { name: 'testing', version: '1.2.3', _resolved: OPTS.registry + tarballPath, _shasum: 'deadbeefc0ffeebad1dea', _hasShrinkwrap: true } - var sr = { + const sr = { name: base.name, version: base.version } - makeTarball({ + return makeTarball({ 'package.json': base, 'npm-shrinkwrap.json': sr - }, function (err, tarData) { - if (err) { throw err } + }).then(tarData => { tnock(t, OPTS.registry).get('/' + tarballPath).reply(200, tarData) - finalizeManifest(base, { + return finalizeManifest(base, { name: base.name, type: 'range' - }, OPTS, function (err, manifest) { - if (err) { throw err } + }, OPTS).then(manifest => { t.deepEqual(manifest._shrinkwrap, sr, 'shrinkwrap successfully added') - t.end() }) }) }) -test('fills in shasum if missing', function (t) { - var tarballPath = 'testing/tarball-1.2.3.tgz' - var base = { +test('fills in shasum if missing', t => { + const tarballPath = 'testing/tarball-1.2.3.tgz' + const base = { name: 'testing', version: '1.2.3', _resolved: OPTS.registry + tarballPath, _hasShrinkwrap: false } - var sr = { + const sr = { name: base.name, version: base.version } - makeTarball({ + return makeTarball({ 'package.json': base, 'npm-shrinkwrap.json': sr - }, function (err, tarData) { - if (err) { throw err } - var sha = crypto.createHash('sha1').update(tarData).digest('hex') + }).then(tarData => { + const sha = crypto.createHash('sha1').update(tarData).digest('hex') tnock(t, OPTS.registry).get('/' + tarballPath).reply(200, tarData) - finalizeManifest(base, { + return finalizeManifest(base, { name: base.name, type: 'range' - }, OPTS, function (err, manifest) { - if (err) { throw err } + }, OPTS).then(manifest => { t.deepEqual(manifest._shasum, sha, 'shasum successfully added') - t.end() }) }) }) -test('fills in `bin` if `directories.bin` string', function (t) { - var tarballPath = 'testing/tarball-1.2.3.tgz' - var base = { +test('fills in `bin` if `directories.bin` string', t => { + const tarballPath = 'testing/tarball-1.2.3.tgz' + const base = { name: 'testing', version: '1.2.3', directories: { @@ -159,38 +151,35 @@ test('fills in `bin` if `directories.bin` string', function (t) { _shasum: 'deadbeefc0ffeebad1dea', _hasShrinkwrap: false } - var sr = { + const sr = { name: base.name, version: base.version } - makeTarball({ + return makeTarball({ 'package.json': base, 'npm-shrinkwrap.json': sr, 'foo/my/bin/x.js': 'x()', 'foo/my/bin/y': 'y()', 'foo/my/bin/z/a.js': 'a() :D', 'foo/my/nope': 'uhhh' - }, function (err, tarData) { - if (err) { throw err } + }).then(tarData => { tnock(t, OPTS.registry).get('/' + tarballPath).reply(200, tarData) - finalizeManifest(base, { + return finalizeManifest(base, { name: base.name, type: 'range' - }, OPTS, function (err, manifest) { - if (err) { throw err } + }, OPTS).then(manifest => { t.deepEqual(manifest.bin, { 'x.js': path.join('foo', 'my', 'bin', 'x.js'), 'y': path.join('foo', 'my', 'bin', 'y'), - 'a.js': path.join('foo', 'my', 'bin', 'z', 'a.js'), + 'a.js': path.join('foo', 'my', 'bin', 'z', 'a.js') }, 'bins successfully calculated') - t.end() }) }) }) -test('fills in `bin` if original was an array', function (t) { - var tarballPath = 'testing/tarball-1.2.3.tgz' - var base = { +test('fills in `bin` if original was an array', t => { + const tarballPath = 'testing/tarball-1.2.3.tgz' + const base = { name: 'testing', version: '1.2.3', bin: ['my/bin1', 'bin2.js'], @@ -201,32 +190,32 @@ test('fills in `bin` if original was an array', function (t) { _shasum: 'deadbeefc0ffeebad1dea', _hasShrinkwrap: false } - finalizeManifest(base, { + return finalizeManifest(base, { name: base.name, type: 'range' - }, OPTS, function (err, manifest) { - if (err) { throw err } + }, OPTS).then(manifest => { t.deepEqual(manifest.bin, { 'bin1': path.join('foo', 'my', 'bin1'), 'bin2.js': path.join('foo', 'bin2.js') }, 'bins successfully calculated') - t.end() }) }) // TODO - this is pending major changes in npm, so not implemented for now. test('manifest returned is immutable + inextensible') -function makeTarball (files, cb) { - var tarData = '' - var pack = tar.pack() +function makeTarball (files) { + let tarData = '' + const pack = tar.pack() Object.keys(files).forEach(function (filename) { pack.entry({ name: 'package/' + filename }, JSON.stringify(files[filename])) }) pack.finalize() - pack.on('data', function (d) { tarData += d }) - pack.on('error', cb) - pack.on('end', function () { cb(null, tarData) }) + return BB.fromNode(cb => { + pack.on('data', function (d) { tarData += d }) + pack.on('error', cb) + pack.on('end', function () { cb(null, tarData) }) + }) } diff --git a/test/registry.manifest.cache.js b/test/registry.manifest.cache.js index 7d5a231..784cbef 100644 --- a/test/registry.manifest.cache.js +++ b/test/registry.manifest.cache.js @@ -1,17 +1,19 @@ 'use strict' -var cache = require('../lib/cache') -var npmlog = require('npmlog') -var test = require('tap').test -var testDir = require('./util/test-dir') -var tnock = require('./util/tnock') +const BB = require('bluebird') -var CACHE = testDir(__filename) -var Manifest = require('../lib/finalize-manifest').Manifest -var manifest = require('../manifest') +const cache = require('../lib/cache') +const npmlog = require('npmlog') +const test = require('tap').test +const testDir = require('./util/test-dir') +const tnock = require('./util/tnock') + +const CACHE = testDir(__filename) +const Manifest = require('../lib/finalize-manifest').Manifest +const manifest = require('../manifest') // This is what the server sends -var BASE = { +const BASE = { name: 'foo', version: '1.2.3', _hasShrinkwrap: false, @@ -21,14 +23,14 @@ var BASE = { } } // This is what's returned by finalize-manifest -var PKG = new Manifest({ +const PKG = new Manifest({ name: 'foo', version: '1.2.3', _hasShrinkwrap: false, _shasum: BASE.dist.shasum, _resolved: BASE.dist.tarball }) -var META = { +const META = { 'dist-tags': { latest: '1.2.3' }, @@ -38,7 +40,7 @@ var META = { } npmlog.level = process.env.LOGLEVEL || 'silent' -var OPTS = { +const OPTS = { cache: CACHE, registry: 'https://mock.reg', log: npmlog, @@ -55,104 +57,88 @@ var OPTS = { } } -test('memoizes identical registry requests', function (t) { +test('memoizes identical registry requests', t => { t.plan(2) - var srv = tnock(t, OPTS.registry) + const srv = tnock(t, OPTS.registry) srv.get('/foo').once().reply(200, META) - manifest('foo@1.2.3', OPTS, function (err, pkg) { - if (err) { throw err } + return manifest('foo@1.2.3', OPTS).then(pkg => { + t.deepEqual(pkg, PKG, 'got a manifest') + return testDir.reset(CACHE) + }).then(() => { + return manifest('foo@1.2.3', OPTS) + }).then(pkg => { t.deepEqual(pkg, PKG, 'got a manifest') - testDir.reset(CACHE, function (err) { - if (err) { throw err } - manifest('foo@1.2.3', OPTS, function (err, pkg) { - if (err) { throw err } - t.deepEqual(pkg, PKG, 'got a manifest') - }) - }) }) }) -test('tag requests memoize versions', function (t) { +test('tag requests memoize versions', t => { t.plan(2) - var srv = tnock(t, OPTS.registry) + const srv = tnock(t, OPTS.registry) srv.get('/foo').once().reply(200, META) - manifest('foo@latest', OPTS, function (err, pkg) { - if (err) { throw err } + return manifest('foo@latest', OPTS).then(pkg => { + t.deepEqual(pkg, PKG, 'got a manifest') + return testDir.reset(CACHE) + }).then(() => { + return manifest('foo@1.2.3', OPTS) + }).then(pkg => { t.deepEqual(pkg, PKG, 'got a manifest') - testDir.reset(CACHE, function (err) { - if (err) { throw err } - manifest('foo@1.2.3', OPTS, function (err, pkg) { - if (err) { throw err } - t.deepEqual(pkg, PKG, 'got a manifest') - }) - }) }) }) -test('tag requests memoize tags', function (t) { +test('tag requests memoize tags', t => { t.plan(2) - var srv = tnock(t, OPTS.registry) + const srv = tnock(t, OPTS.registry) srv.get('/foo').once().reply(200, META) - manifest('foo@latest', OPTS, function (err, pkg) { - if (err) { throw err } + return manifest('foo@latest', OPTS).then(pkg => { + t.deepEqual(pkg, PKG, 'got a manifest') + return testDir.reset(CACHE) + }).then(() => { + return manifest('foo@latest', OPTS) + }).then(pkg => { t.deepEqual(pkg, PKG, 'got a manifest') - testDir.reset(CACHE, function (err) { - if (err) { throw err } - manifest('foo@latest', OPTS, function (err, pkg) { - if (err) { throw err } - t.deepEqual(pkg, PKG, 'got a manifest') - }) - }) }) }) test('memoization is scoped to a given cache') -test('inflights concurrent requests', function (t) { - t.plan(2) - var srv = tnock(t, OPTS.registry) +test('inflights concurrent requests', t => { + const srv = tnock(t, OPTS.registry) srv.get('/foo').once().reply(200, META) - manifest('foo@1.2.3', OPTS, function (err, pkg) { - if (err) { throw err } - t.deepEqual(pkg, PKG, 'got a manifest') - }) - - manifest('foo@1.2.3', OPTS, function (err, pkg) { - if (err) { throw err } - t.deepEqual(pkg, PKG, 'got a manifest') - }) + return BB.join( + manifest('foo@1.2.3', OPTS).then(pkg => { + t.deepEqual(pkg, PKG, 'got a manifest') + }), + manifest('foo@1.2.3', OPTS).then(pkg => { + t.deepEqual(pkg, PKG, 'got a manifest') + }) + ) }) -test('supports fetching from an optional cache', function (t) { +test('supports fetching from an optional cache', t => { tnock(t, OPTS.registry) - var key = cache.key('registry-request', OPTS.registry + '/foo') - cache.put(CACHE, key, JSON.stringify(META), OPTS, function (err) { - if (err) { throw err } - manifest('foo@1.2.3', OPTS, function (err, pkg) { - if (err) { throw err } + const key = cache.key('registry-request', OPTS.registry + '/foo') + return cache.put(CACHE, key, JSON.stringify(META), OPTS).then(() => { + return manifest('foo@1.2.3', OPTS).then(pkg => { t.deepEqual(pkg, PKG) - t.end() }) }) }) -test('falls back to registry if cache entry missing', function (t) { - var opts = { +test('falls back to registry if cache entry missing', t => { + const opts = { registry: OPTS.registry, log: OPTS.log, retry: OPTS.retry, cache: CACHE } - var srv = tnock(t, opts.registry) + const srv = tnock(t, opts.registry) srv.get('/foo').reply(200, META) - manifest('foo@1.2.3', opts, function (err, pkg) { - if (err) { throw err } + return manifest('foo@1.2.3', opts).then(pkg => { t.deepEqual(pkg, PKG) - t.end() }) }) diff --git a/test/registry.manifest.js b/test/registry.manifest.js index cac27b0..6ee91bf 100644 --- a/test/registry.manifest.js +++ b/test/registry.manifest.js @@ -1,13 +1,15 @@ 'use strict' -var npmlog = require('npmlog') -var test = require('tap').test -var tnock = require('./util/tnock') +const BB = require('bluebird') -var Manifest = require('../lib/finalize-manifest').Manifest -var manifest = require('../manifest') +const npmlog = require('npmlog') +const test = require('tap').test +const tnock = require('./util/tnock') -var BASE = { +const Manifest = require('../lib/finalize-manifest').Manifest +const manifest = require('../manifest') + +const BASE = { name: 'foo', version: '1.2.3', _hasShrinkwrap: false, @@ -19,7 +21,7 @@ var BASE = { } } -var META = { +const META = { name: 'foo', 'dist-tags': { latest: '1.2.3', lts: '1.2.1' }, versions: { @@ -54,10 +56,10 @@ var META = { } } } -var PKG = new Manifest(BASE) +const PKG = new Manifest(BASE) npmlog.level = process.env.LOGLEVEL || 'silent' -var OPTS = { +const OPTS = { registry: 'https://mock.reg', log: npmlog, retry: { @@ -68,76 +70,64 @@ var OPTS = { } } -test('fetches version from registry', function (t) { - var srv = tnock(t, OPTS.registry) +test('fetches version from registry', t => { + const srv = tnock(t, OPTS.registry) srv.get('/foo').reply(200, META) - manifest('foo@1.2.3', OPTS, function (err, pkg) { - if (err) { throw err } + return manifest('foo@1.2.3', OPTS).then(pkg => { t.deepEqual(pkg, PKG, 'got manifest from version') - t.end() }) }) -test('fetchest tag from registry', function (t) { - var srv = tnock(t, OPTS.registry) +test('fetchest tag from registry', t => { + const srv = tnock(t, OPTS.registry) srv.get('/foo').reply(200, META) - manifest('foo@latest', OPTS, function (err, pkg) { - if (err) { throw err } + return manifest('foo@latest', OPTS).then(pkg => { t.deepEqual(pkg, PKG, 'got manifest from tag') - t.end() }) }) -test('fetches version from scoped registry', function (t) { - var srv = tnock(t, OPTS.registry) +test('fetches version from scoped registry', t => { + const srv = tnock(t, OPTS.registry) srv.get('/@usr%2ffoo').reply(200, META) - manifest('@usr/foo@1.2.3', OPTS, function (err, pkg) { - if (err) { throw err } + return manifest('@usr/foo@1.2.3', OPTS).then(pkg => { t.deepEqual(pkg, PKG, 'got scoped manifest from version') - t.end() }) }) -test('fetches tag from scoped registry', function (t) { - var srv = tnock(t, OPTS.registry) +test('fetches tag from scoped registry', t => { + const srv = tnock(t, OPTS.registry) srv.get('/@usr%2ffoo').reply(200, META) - manifest('@usr/foo@latest', OPTS, function (err, pkg) { - if (err) { throw err } + return manifest('@usr/foo@latest', OPTS).then(pkg => { t.deepEqual(pkg, PKG, 'got scoped manifest from tag') - t.end() }) }) -test('fetches manifest from registry by range', function (t) { - var srv = tnock(t, OPTS.registry) +test('fetches manifest from registry by range', t => { + const srv = tnock(t, OPTS.registry) srv.get('/foo').reply(200, META) - manifest('foo@^1.2.0', OPTS, function (err, pkg) { - if (err) { throw err } + return manifest('foo@^1.2.0', OPTS).then(pkg => { // Not 1.2.4 because 1.2.3 is `latest` t.deepEqual(pkg, new Manifest(META.versions['1.2.3']), 'picked right manifest') - t.end() }) }) -test('fetches manifest from scoped registry by range', function (t) { - var srv = tnock(t, OPTS.registry) +test('fetches manifest from scoped registry by range', t => { + const srv = tnock(t, OPTS.registry) srv.get('/@usr%2ffoo').reply(200, META) - manifest('@usr/foo@^1.2.0', OPTS, function (err, pkg) { - if (err) { throw err } + return manifest('@usr/foo@^1.2.0', OPTS).then(pkg => { t.deepEqual(pkg, new Manifest(META.versions['1.2.3']), 'got scoped manifest from version') - t.end() }) }) -test('sends auth token if passed in opts', function (t) { - var TOKEN = 'deadbeef' - var opts = { +test('sends auth token if passed in opts', t => { + const TOKEN = 'deadbeef' + const opts = { log: OPTS.log, registry: OPTS.registry, auth: { @@ -147,84 +137,75 @@ test('sends auth token if passed in opts', function (t) { } } - var srv = tnock(t, OPTS.registry) + const srv = tnock(t, OPTS.registry) srv.get( '/foo' ).matchHeader( 'authorization', 'Bearer ' + TOKEN ).reply(200, META) - manifest('foo@1.2.3', opts, function (err, pkg) { - if (err) { throw err } + return manifest('foo@1.2.3', opts).then(pkg => { t.deepEqual(pkg, PKG, 'got manifest from version') - t.end() }) }) -test('treats options as optional', function (t) { - var srv = tnock(t, 'https://registry.npmjs.org') +test('treats options as optional', t => { + const srv = tnock(t, 'https://registry.npmjs.org') srv.get('/foo').reply(200, META) - manifest('foo@1.2.3', function (err, pkg) { - if (err) { throw err } + return manifest('foo@1.2.3').then(pkg => { t.deepEqual(pkg, PKG, 'used default options') - t.end() }) }) -test('uses scope from spec for registry lookup', function (t) { - var opts = { +test('uses scope from spec for registry lookup', t => { + const opts = { '@myscope:registry': OPTS.registry, // package scope takes priority scope: '@otherscope' } - var srv = tnock(t, OPTS.registry) + const srv = tnock(t, OPTS.registry) srv.get('/@myscope%2ffoo').reply(200, META) - manifest('@myscope/foo@1.2.3', opts, function (err, pkg) { - if (err) { throw err } + return manifest('@myscope/foo@1.2.3', opts).then(pkg => { t.deepEqual(pkg, PKG, 'used scope to pick registry') - t.end() }) }) -test('uses scope opt for registry lookup', function (t) { - t.plan(2) - var srv = tnock(t, OPTS.registry) +test('uses scope opt for registry lookup', t => { + const srv = tnock(t, OPTS.registry) srv.get('/foo').reply(200, META) - manifest('foo@1.2.3', { - '@myscope:registry': OPTS.registry, - scope: '@myscope', - // scope option takes priority - registry: 'nope' - }, function (err, pkg) { - if (err) { throw err } - t.deepEqual(pkg, PKG, 'used scope to pick registry') - }) - srv.get('/bar').reply(200, META) - manifest('bar@latest', { - '@myscope:registry': OPTS.registry, - scope: 'myscope' // @ auto-inserted - }, function (err, pkg) { - if (err) { throw err } - t.deepEqual(pkg, PKG, 'scope @ was auto-inserted') - }) + + return BB.join( + manifest('foo@1.2.3', { + '@myscope:registry': OPTS.registry, + scope: '@myscope', + // scope option takes priority + registry: 'nope' + }).then(pkg => { + t.deepEqual(pkg, PKG, 'used scope to pick registry') + }), + manifest('bar@latest', { + '@myscope:registry': OPTS.registry, + scope: 'myscope' // @ auto-inserted + }).then(pkg => { + t.deepEqual(pkg, PKG, 'scope @ was auto-inserted') + }) + ) }) -test('defaults to registry.npmjs.org if no option given', function (t) { - var srv = tnock(t, 'https://registry.npmjs.org') +test('defaults to registry.npmjs.org if no option given', t => { + const srv = tnock(t, 'https://registry.npmjs.org') srv.get('/foo').reply(200, META) - manifest('foo@1.2.3', { registry: undefined }, function (err, pkg) { - if (err) { throw err } + return manifest('foo@1.2.3', { registry: undefined }).then(pkg => { t.deepEqual(pkg, PKG, 'used npm registry') - t.end() }) }) -test('supports scoped auth', function (t) { - var TOKEN = 'deadbeef' - var opts = { +test('supports scoped auth', t => { + const TOKEN = 'deadbeef' + const opts = { scope: 'myscope', '@myscope:registry': OPTS.registry, auth: { @@ -233,24 +214,21 @@ test('supports scoped auth', function (t) { } } } - var srv = tnock(t, OPTS.registry) + const srv = tnock(t, OPTS.registry) srv.get( '/foo' ).matchHeader( 'authorization', 'Bearer ' + TOKEN ).reply(200, META) - manifest('foo@1.2.3', opts, function (err, pkg) { - if (err) { throw err } + return manifest('foo@1.2.3', opts).then(pkg => { t.deepEqual(pkg, PKG, 'used scope to pick registry and auth') - t.end() }) }) -test('package requests are case-sensitive', function (t) { - t.plan(2) - var srv = tnock(t, OPTS.registry) +test('package requests are case-sensitive', t => { + const srv = tnock(t, OPTS.registry) - var CASEDBASE = { + const CASEDBASE = { name: 'Foo', version: '1.2.3', _hasShrinkwrap: false, @@ -261,45 +239,44 @@ test('package requests are case-sensitive', function (t) { tarball: 'https://foo.bar/x.tgz' } } - var CASEDPKG = new Manifest(CASEDBASE) + const CASEDPKG = new Manifest(CASEDBASE) srv.get('/Foo').reply(200, { versions: { '1.2.3': CASEDBASE } }) - manifest('Foo@1.2.3', OPTS, function (err, pkg) { - if (err) { throw err } - t.deepEqual(pkg, CASEDPKG, 'got Cased package') - }) - srv.get('/foo').reply(200, META) - manifest('foo@1.2.3', OPTS, function (err, pkg) { - if (err) { throw err } - t.deepEqual(pkg, PKG, 'got lowercased package') - }) + + return BB.join( + manifest('Foo@1.2.3', OPTS).then(pkg => { + t.deepEqual(pkg, CASEDPKG, 'got Cased package') + }), + manifest('foo@1.2.3', OPTS).then(pkg => { + t.deepEqual(pkg, PKG, 'got lowercased package') + }) + ) }) -test('handles server-side case-normalization', function (t) { - t.plan(2) - var srv = tnock(t, OPTS.registry) +test('handles server-side case-normalization', t => { + const srv = tnock(t, OPTS.registry) srv.get('/Cased').reply(200, META) - manifest('Cased@1.2.3', OPTS, function (err, pkg) { - if (err) { throw err } - t.deepEqual(pkg, PKG, 'got Cased package') - }) - srv.get('/cased').reply(200, META) - manifest('cased@latest', OPTS, function (err, pkg) { - if (err) { throw err } - t.deepEqual(pkg, PKG, 'got lowercased package') - }) + + return BB.join( + manifest('Cased@1.2.3', OPTS).then(pkg => { + t.deepEqual(pkg, PKG, 'got Cased package') + }), + manifest('cased@latest', OPTS).then(pkg => { + t.deepEqual(pkg, PKG, 'got lowercased package') + }) + ) }) -test('recovers from request errors', function (t) { +test('recovers from request errors', t => { t.plan(4) - var srv = tnock(t, OPTS.registry) - var opts = { + const srv = tnock(t, OPTS.registry) + const opts = { log: OPTS.log, registry: OPTS.registry, retry: { @@ -322,8 +299,7 @@ test('recovers from request errors', function (t) { return body }) - manifest('foo@1.2.3', opts, function (err, pkg) { - if (err) { throw err } + manifest('foo@1.2.3', opts).then(pkg => { t.deepEqual(pkg, PKG, 'got a manifest') }) }) diff --git a/test/registry.manifest.shrinkwrap.js b/test/registry.manifest.shrinkwrap.js index 57a23b7..c391b3b 100644 --- a/test/registry.manifest.shrinkwrap.js +++ b/test/registry.manifest.shrinkwrap.js @@ -1,14 +1,14 @@ 'use strict' -var npmlog = require('npmlog') -var tar = require('tar-stream') -var test = require('tap').test -var tnock = require('./util/tnock') +const npmlog = require('npmlog') +const tar = require('tar-stream') +const test = require('tap').test +const tnock = require('./util/tnock') -var manifest = require('../manifest') +const manifest = require('../manifest') npmlog.level = process.env.LOGLEVEL || 'silent' -var OPTS = { +const OPTS = { registry: 'https://mock.reg', log: npmlog, retry: { @@ -19,17 +19,17 @@ var OPTS = { } } -var PKG = { +const PKG = { name: 'foo', version: '1.2.3' } -var SHRINKWRAP = { +const SHRINKWRAP = { name: 'foo', version: '1.2.3' } -var META = { +const META = { name: 'foo', 'dist-tags': { latest: '1.2.3', lts: '1.2.1' }, versions: { @@ -41,9 +41,9 @@ var META = { } } -var TARBALL = '' -test('tarball setup', function (t) { - var pack = tar.pack() +let TARBALL = '' +test('tarball setup', t => { + const pack = tar.pack() pack.entry({ name: 'package/npm-shrinkwrap.json' }, JSON.stringify(SHRINKWRAP)) pack.entry({ name: 'package/package.json' }, JSON.stringify(PKG)) pack.finalize() @@ -52,16 +52,14 @@ test('tarball setup', function (t) { pack.on('end', function () { t.end() }) }) -test('fetches shrinkwrap data if missing + required', function (t) { +test('fetches shrinkwrap data if missing + required', t => { var srv = tnock(t, OPTS.registry) srv.get('/foo').reply(200, META) srv.get('/foo/-/foo-1.2.3.tgz').reply(200, TARBALL) - manifest('foo@1.2.3', OPTS, function (err, pkg) { - if (err) { throw err } + return manifest('foo@1.2.3', OPTS).then(pkg => { t.ok(pkg, 'got a package manifest') t.deepEqual(pkg._shrinkwrap, SHRINKWRAP, 'got a shrinkwrap') - t.end() }) }) diff --git a/test/registry.pick-manifest.js b/test/registry.pick-manifest.js index addb14a..82e593f 100644 --- a/test/registry.pick-manifest.js +++ b/test/registry.pick-manifest.js @@ -1,15 +1,17 @@ 'use strict' -var test = require('tap').test +const BB = require('bluebird') -var pickManifest = require('../lib/registry/pick-manifest') +const test = require('tap').test + +const pickManifest = require('../lib/registry/pick-manifest') function spec (selector, type) { return { spec: selector, type: type || 'range' } } -test('basic carat range selection', function (t) { - var metadata = { +test('basic carat range selection', t => { + const metadata = { versions: { '1.0.0': { version: '1.0.0' }, '1.0.1': { version: '1.0.1' }, @@ -17,15 +19,13 @@ test('basic carat range selection', function (t) { '2.0.0': { version: '2.0.0' } } } - pickManifest(metadata, spec('^1.0.0'), {}, function (err, manifest) { - if (err) { throw err } + return pickManifest(metadata, spec('^1.0.0'), {}).then(manifest => { t.equal(manifest.version, '1.0.2', 'picked the right manifest using ^') - t.end() }) }) -test('basic tilde range selection', function (t) { - var metadata = { +test('basic tilde range selection', t => { + const metadata = { versions: { '1.0.0': { version: '1.0.0' }, '1.0.1': { version: '1.0.1' }, @@ -33,15 +33,13 @@ test('basic tilde range selection', function (t) { '2.0.0': { version: '2.0.0' } } } - pickManifest(metadata, spec('~1.0.0'), {}, function (err, manifest) { - if (err) { throw err } + return pickManifest(metadata, spec('~1.0.0'), {}).then(manifest => { t.equal(manifest.version, '1.0.2', 'picked the right manifest using ~') - t.end() }) }) -test('basic mathematical range selection', function (t) { - var metadata = { +test('basic mathematical range selection', t => { + const metadata = { versions: { '1.0.0': { version: '1.0.0' }, '1.0.1': { version: '1.0.1' }, @@ -49,15 +47,13 @@ test('basic mathematical range selection', function (t) { '2.0.0': { version: '2.0.0' } } } - pickManifest(metadata, spec('>=1.0.0 <2'), {}, function (err, manifest) { - if (err) { throw err } + return pickManifest(metadata, spec('>=1.0.0 <2'), {}, manifest => { t.equal(manifest.version, '1.0.2', 'picked the right manifest using mathematical range') - t.end() }) }) -test('basic version selection', function (t) { - var metadata = { +test('basic version selection', t => { + const metadata = { versions: { '1.0.0': { version: '1.0.0' }, '1.0.1': { version: '1.0.1' }, @@ -65,16 +61,15 @@ test('basic version selection', function (t) { '2.0.0': { version: '2.0.0' } } } - pickManifest(metadata, spec('1.0.0', 'version'), { - }, function (err, manifest) { - if (err) { throw err } + return pickManifest( + metadata, spec('1.0.0', 'version'), {} + ).then(manifest => { t.equal(manifest.version, '1.0.0', 'picked the right manifest using specific version') - t.end() }) }) -test('basic tag selection', function (t) { - var metadata = { +test('basic tag selection', t => { + const metadata = { 'dist-tags': { foo: '1.0.1' }, @@ -85,15 +80,13 @@ test('basic tag selection', function (t) { '2.0.0': { version: '2.0.0' } } } - pickManifest(metadata, spec('foo', 'tag'), {}, function (err, manifest) { - if (err) { throw err } + return pickManifest(metadata, spec('foo', 'tag'), {}).then(manifest => { t.equal(manifest.version, '1.0.1', 'picked the right manifest using tag') - t.end() }) }) -test('errors if a non-registry spec is provided', function (t) { - var metadata = { +test('errors if a non-registry spec is provided', t => { + const metadata = { 'dist-tags': { foo: '1.0.1' }, @@ -101,52 +94,59 @@ test('errors if a non-registry spec is provided', function (t) { '1.0.1': { version: '1.0.1' } } } - pickManifest(metadata, {spec: 'foo', type: 'uhhh'}, {}, function (err) { - t.ok(err, 'errored on bad spec type') - t.match(err.message, /Only tag, version, and range are supported/) - t.end() - }) + return pickManifest( + metadata, {spec: 'foo', type: 'uhhh'}, {} + ).then( + () => { throw new Error('expected error') }, + err => { + t.ok(err, 'errored on bad spec type') + t.match(err.message, /Only tag, version, and range are supported/) + } + ) }) -test('skips any invalid version keys', function (t) { +test('skips any invalid version keys', t => { // Various third-party registries are prone to having trash as // keys. npm simply skips them. Yay robustness. - var metadata = { + const metadata = { versions: { '1.0.0': { version: '1.0.0' }, 'lol ok': { version: '1.0.1' } } } - t.plan(4) - pickManifest(metadata, spec('^1.0.0'), {}, function (err, manifest) { - if (err) { throw err } - t.equal(manifest.version, '1.0.0', 'avoided bad key') - }) - pickManifest(metadata, spec('^1.0.1'), {}, function (err, manifest) { - t.notOk(manifest, 'no manifest returned') - t.ok(err, 'got an error') - t.equal(err.code, 'ENOENT', 'no matching specs') - }) + return BB.join( + pickManifest(metadata, spec('^1.0.0'), {}).then(manifest => { + t.equal(manifest.version, '1.0.0', 'avoided bad key') + }), + pickManifest(metadata, spec('^1.0.1'), {}).then( + () => { throw new Error('expected a failure') }, + err => { + t.ok(err, 'got an error') + t.equal(err.code, 'ENOENT', 'no matching specs') + } + ) + ) }) -test('ENOENT if range does not match anything', function (t) { - var metadata = { +test('ENOENT if range does not match anything', t => { + const metadata = { versions: { '1.0.0': { version: '1.0.0' }, '2.0.0': { version: '2.0.0' }, '2.0.5': { version: '2.0.5' } } } - pickManifest(metadata, spec('^2.1.0'), {}, function (err, manifest) { - t.ok(err, 'got an error') - t.equal(err.code, 'ENOENT', 'useful error code returned.') - t.notOk(manifest, 'no manifest returned.') - t.end() - }) + return pickManifest(metadata, spec('^2.1.0'), {}).then( + () => { throw new Error('expected a failure') }, + err => { + t.ok(err, 'got an error') + t.equal(err.code, 'ENOENT', 'useful error code returned.') + } + ) }) -test('if `defaultTag` matches a given range, use it', function (t) { - var metadata = { +test('if `defaultTag` matches a given range, use it', t => { + const metadata = { 'dist-tags': { foo: '1.0.1', latest: '1.0.0' @@ -158,27 +158,25 @@ test('if `defaultTag` matches a given range, use it', function (t) { '2.0.0': { version: '2.0.0' } } } - t.plan(3) - pickManifest(metadata, spec('^1.0.0'), { - defaultTag: 'foo' - }, function (err, manifest) { - if (err) { throw err } - t.equal(manifest.version, '1.0.1', 'picked the version for foo') - }) - pickManifest(metadata, spec('^2.0.0'), { - defaultTag: 'foo' - }, function (err, manifest) { - if (err) { throw err } - t.equal(manifest.version, '2.0.0', 'no match, no foo') - }) - pickManifest(metadata, spec('^1.0.0'), {}, function (err, manifest) { - if (err) { throw err } - t.equal(manifest.version, '1.0.0', 'default to `latest`') - }) + return BB.join( + pickManifest(metadata, spec('^1.0.0'), { + defaultTag: 'foo' + }).then(manifest => { + t.equal(manifest.version, '1.0.1', 'picked the version for foo') + }), + pickManifest(metadata, spec('^2.0.0'), { + defaultTag: 'foo' + }).then(manifest => { + t.equal(manifest.version, '2.0.0', 'no match, no foo') + }), + pickManifest(metadata, spec('^1.0.0'), {}).then(manifest => { + t.equal(manifest.version, '1.0.0', 'default to `latest`') + }) + ) }) -test('* ranges use `defaultTag` if no versions match', function (t) { - var metadata = { +test('* ranges use `defaultTag` if no versions match', t => { + const metadata = { 'dist-tags': { latest: '1.0.0-pre.0', beta: '2.0.0-beta.0' @@ -190,27 +188,33 @@ test('* ranges use `defaultTag` if no versions match', function (t) { '2.0.0-beta.1': { version: '2.0.0-beta.1' } } } - t.plan(2) - pickManifest(metadata, spec('*'), { - defaultTag: 'beta' - }, function (err, manifest) { - if (err) { throw err } - t.equal(manifest.version, '2.0.0-beta.0', 'used defaultTag for all-prerelease splat.') - }) - pickManifest(metadata, spec('*'), {}, function (err, manifest) { - if (err) { throw err } - t.equal(manifest.version, '1.0.0-pre.0', 'defaulted to `latest`.') - }) + return BB.join( + pickManifest(metadata, spec('*'), { + defaultTag: 'beta' + }).then(manifest => { + t.equal(manifest.version, '2.0.0-beta.0', 'used defaultTag for all-prerelease splat.') + }), + pickManifest(metadata, spec('*'), {}).then(manifest => { + t.equal(manifest.version, '1.0.0-pre.0', 'defaulted to `latest`.') + }) + ) }) -test('errors if metadata has no versions', function (t) { - t.plan(4) - pickManifest({versions: {}}, spec('^1.0.0'), {}, function (err) { - t.ok(err, 'got an error') - t.equal(err.code, 'ENOVERSIONS', 'useful error code') - }) - pickManifest({}, spec('^1.0.0'), {}, function (err) { - t.ok(err, 'got an error') - t.equal(err.code, 'ENOVERSIONS', 'useful error code') - }) +test('errors if metadata has no versions', t => { + return BB.join( + pickManifest({versions: {}}, spec('^1.0.0'), {}).then( + () => { throw new Error('expected an error') }, + err => { + t.ok(err, 'got an error') + t.equal(err.code, 'ENOVERSIONS', 'useful error code') + } + ), + pickManifest({}, spec('^1.0.0'), {}).then( + () => { throw new Error('expected an error') }, + err => { + t.ok(err, 'got an error') + t.equal(err.code, 'ENOVERSIONS', 'useful error code') + } + ) + ) }) diff --git a/test/registry.tarball.js b/test/registry.tarball.js index f4b30e1..d3c9b6b 100644 --- a/test/registry.tarball.js +++ b/test/registry.tarball.js @@ -1,16 +1,18 @@ 'use strict' -var finished = require('mississippi').finished -var mockTar = require('./util/mock-tarball') -var npmlog = require('npmlog') -var test = require('tap').test -var tnock = require('./util/tnock') +const BB = require('bluebird') + +const finished = BB.promisify(require('mississippi').finished) +const mockTar = require('./util/mock-tarball') +const npmlog = require('npmlog') +const test = require('tap').test +const tnock = require('./util/tnock') require('./util/test-dir')(__filename) -var tarball = require('../lib/registry/tarball') +const tarball = require('../lib/registry/tarball') -var BASE = { +const BASE = { name: 'foo', version: '1.2.3', _hasShrinkwrap: false, @@ -20,7 +22,7 @@ var BASE = { } } -var META = { +const META = { name: 'foo', 'dist-tags': { latest: '1.2.3' }, versions: { @@ -29,7 +31,7 @@ var META = { } npmlog.level = process.env.LOGLEVEL || 'silent' -var OPTS = { +const OPTS = { log: npmlog, registry: 'https://my.mock.registry/', retry: { @@ -41,31 +43,30 @@ var OPTS = { } test('basic tarball streaming', function (t) { - var pkg = { + const pkg = { 'package.json': JSON.stringify({ name: 'foo', version: '1.2.3' }), 'index.js': 'console.log("hello world!")' } - mockTar(pkg, function (err, tarData) { - if (err) { throw err } - var srv = tnock(t, OPTS.registry) + return mockTar(pkg).then(tarData => { + const srv = tnock(t, OPTS.registry) srv.get('/foo').reply(200, META) srv.get('/foo/-/foo-1.2.3.tgz').reply(200, tarData) - var data = '' - finished(tarball({ - type: 'range', - raw: 'foo@^1.2.3', - name: 'foo', - escapedName: 'foo', - rawSpec: '^1.2.3', - spec: '>=1.2.3 <2.0.0', - scope: null - }, OPTS).on('data', function (d) { data += d }), function (err) { - if (err) { throw err } + let data = '' + return finished( + tarball({ + type: 'range', + raw: 'foo@^1.2.3', + name: 'foo', + escapedName: 'foo', + rawSpec: '^1.2.3', + spec: '>=1.2.3 <2.0.0', + scope: null + }, OPTS).on('data', d => { data += d }) + ).then(() => { t.equal(data, tarData, 'fetched tarball data matches one from server') - t.done() }) }) }) diff --git a/test/util/mock-tarball.js b/test/util/mock-tarball.js index 8e01967..c1c89b9 100644 --- a/test/util/mock-tarball.js +++ b/test/util/mock-tarball.js @@ -1,19 +1,17 @@ 'use strict' -var pipeline = require('mississippi').pipeline -var tar = require('tar-stream') -var zlib = require('zlib') +const BB = require('bluebird') + +const pipeline = require('mississippi').pipeline +const tar = require('tar-stream') +const zlib = require('zlib') module.exports = makeTarball -function makeTarball (files, opts, cb) { - if (!cb) { - cb = opts - opts = null - } +function makeTarball (files, opts) { opts = opts || {} - var pack = tar.pack() + const pack = tar.pack() Object.keys(files).forEach(function (filename) { - var entry = files[filename] + const entry = files[filename] pack.entry({ name: (opts.noPrefix ? '' : 'package/') + filename, type: entry.type, @@ -30,20 +28,22 @@ function makeTarball (files, opts, cb) { : files[filename].data) }) pack.finalize() - if (opts.stream && opts.gzip) { - return cb(null, pipeline(pack, zlib.createGzip())) - } else if (opts.stream) { - return cb(null, pack) - } else { - var tarData = '' - pack.on('data', function (d) { tarData += d }) - pack.on('error', cb) - pack.on('end', function () { - if (opts.gzip) { - zlib.gzip(tarData, cb) - } else { - cb(null, tarData) - } - }) - } + return BB.fromNode(cb => { + if (opts.stream && opts.gzip) { + return cb(null, pipeline(pack, zlib.createGzip())) + } else if (opts.stream) { + return cb(null, pack) + } else { + let tarData = '' + pack.on('data', function (d) { tarData += d }) + pack.on('error', cb) + pack.on('end', function () { + if (opts.gzip) { + zlib.gzip(tarData, cb) + } else { + cb(null, tarData) + } + }) + } + }) } diff --git a/test/util/test-dir.js b/test/util/test-dir.js index 8390f32..deea9ac 100644 --- a/test/util/test-dir.js +++ b/test/util/test-dir.js @@ -1,21 +1,20 @@ 'use strict' -var mkdirp = require('mkdirp') -var path = require('path') -var rimraf = require('rimraf') -var tap = require('tap') +const BB = require('bluebird') -var cacheDir = path.resolve(__dirname, '../cache') +const mkdirp = require('mkdirp') +const path = require('path') +const rimraf = require('rimraf') +const tap = require('tap') + +const cacheDir = path.resolve(__dirname, '../cache') module.exports = testDir function testDir (filename) { - var base = path.basename(filename, '.js') - var dir = path.join(cacheDir, base) - tap.beforeEach(function (cb) { - reset(dir, function (err) { - if (err) { throw err } - cb() - }) + const base = path.basename(filename, '.js') + const dir = path.join(cacheDir, base) + tap.beforeEach(function () { + return reset(dir) }) if (!process.env.KEEPCACHE) { tap.tearDown(function () { @@ -33,14 +32,16 @@ function testDir (filename) { } module.exports.reset = reset -function reset (testDir, cb) { +function reset (testDir) { process.chdir(__dirname) - rimraf(testDir, function (err) { - if (err) { return cb(err) } - mkdirp(testDir, function (err) { + return BB.fromNode(cb => { + rimraf(testDir, function (err) { if (err) { return cb(err) } - process.chdir(testDir) - cb() + mkdirp(testDir, function (err) { + if (err) { return cb(err) } + process.chdir(testDir) + cb() + }) }) }) } diff --git a/test/util/tnock.js b/test/util/tnock.js index 7bed3d1..07a23fe 100644 --- a/test/util/tnock.js +++ b/test/util/tnock.js @@ -1,7 +1,7 @@ 'use strict' var nock = require('nock') -var clearMemoized = require('../../lib/cache')._clearMemoized +var clearMemoized = require('../../lib/cache').clearMemoized module.exports = tnock function tnock (t, host) {