diff --git a/lib/install/inflate-shrinkwrap.js b/lib/install/inflate-shrinkwrap.js index cd1722abe37..e8e78e714ce 100644 --- a/lib/install/inflate-shrinkwrap.js +++ b/lib/install/inflate-shrinkwrap.js @@ -1,9 +1,9 @@ 'use strict' -var url = require('url') var asyncMap = require('slide').asyncMap var validate = require('aproba') var iferr = require('iferr') var realizeShrinkwrapSpecifier = require('./realize-shrinkwrap-specifier.js') +var isRegistrySpecifier = require('./is-registry-specifier.js') var fetchPackageMetadata = require('../fetch-package-metadata.js') var annotateMetadata = require('../fetch-package-metadata.js').annotateMetadata var addShrinkwrap = require('../fetch-package-metadata.js').addShrinkwrap @@ -14,16 +14,20 @@ var createChild = require('./node.js').create var moduleName = require('../utils/module-name.js') var childPath = require('../utils/child-path.js') -var inflateShrinkwrap = module.exports = function (tree, swdeps, finishInflating) { - validate('OOF', arguments) +module.exports = function (tree, swdeps, finishInflating) { if (!npm.config.get('shrinkwrap')) return finishInflating() + return inflateShrinkwrap(tree.path, tree, swdeps, finishInflating) +} + +function inflateShrinkwrap (topPath, tree, swdeps, finishInflating) { + validate('SOOF', arguments) var onDisk = {} tree.children.forEach(function (child) { onDisk[moduleName(child)] = child }) tree.children = [] return asyncMap(Object.keys(swdeps), doRealizeAndInflate, finishInflating) function doRealizeAndInflate (name, next) { - return realizeShrinkwrapSpecifier(name, swdeps[name], tree.path, iferr(next, andInflate(name, next))) + return realizeShrinkwrapSpecifier(name, swdeps[name], topPath, iferr(next, andInflate(name, next))) } function andInflate (name, next) { @@ -33,15 +37,15 @@ var inflateShrinkwrap = module.exports = function (tree, swdeps, finishInflating var child = onDisk[name] if (child && (child.fromShrinkwrap || (sw.resolved && child.package._resolved === sw.resolved) || - (sw.from && url.parse(sw.from).protocol && child.package._from === sw.from) || + (!isRegistrySpecifier(requested) && child.package._from === sw.from) || child.package.version === sw.version)) { if (!child.fromShrinkwrap) child.fromShrinkwrap = requested.raw tree.children.push(child) - annotateMetadata(child.package, requested, requested.raw, tree.path) - return inflateShrinkwrap(child, dependencies || {}, next) + annotateMetadata(child.package, requested, requested.raw, topPath) + return inflateShrinkwrap(topPath, child, dependencies || {}, next) } else { var from = sw.from || requested.raw - return fetchPackageMetadata(requested, tree.path, iferr(next, andAddShrinkwrap(from, dependencies, next))) + return fetchPackageMetadata(requested, topPath, iferr(next, andAddShrinkwrap(from, dependencies, next))) } } } @@ -75,7 +79,7 @@ var inflateShrinkwrap = module.exports = function (tree, swdeps, finishInflating delete pkg._bundled inflateBundled(child, child.children) } - inflateShrinkwrap(child, dependencies || {}, next) + inflateShrinkwrap(topPath, child, dependencies || {}, next) } } } diff --git a/lib/install/realize-shrinkwrap-specifier.js b/lib/install/realize-shrinkwrap-specifier.js index f444135e178..0c491a60289 100644 --- a/lib/install/realize-shrinkwrap-specifier.js +++ b/lib/install/realize-shrinkwrap-specifier.js @@ -1,11 +1,25 @@ 'use strict' -var url = require('url') +var realizePackageSpecifier = require('realize-package-specifier') +var isRegistrySpecifier = require('./is-registry-specifier.js') module.exports = function (name, sw, where, cb) { - var spec = sw.resolved - ? name + '@' + sw.resolved - : (sw.from && url.parse(sw.from).protocol) - ? name + '@' + sw.from - : name + '@' + sw.version - process.nextTick(cb, null, spec) + function lookup (ver, cb) { + realizePackageSpecifier(name + '@' + ver, where, cb) + } + if (sw.resolved) { + return lookup(sw.resolved, cb) + } else if (sw.from) { + return lookup(sw.from, function (err, spec) { + if (err || isRegistrySpecifier(spec)) { + return thenUseVersion() + } else { + return cb(null, spec) + } + }) + } else { + return thenUseVersion() + } + function thenUseVersion () { + lookup(sw.version, cb) + } } diff --git a/test/tap/shrinkwrap-local-dependency.js b/test/tap/shrinkwrap-local-dependency.js index 8d7c0712f9a..7b68083fcd6 100644 --- a/test/tap/shrinkwrap-local-dependency.js +++ b/test/tap/shrinkwrap-local-dependency.js @@ -1,92 +1,120 @@ var test = require('tap').test var path = require('path') var fs = require('fs') -var osenv = require('osenv') var rimraf = require('rimraf') -var mkdirp = require('mkdirp') var common = require('../common-tap.js') - -var PKG_DIR = path.resolve(__dirname, 'shrinkwrap-local-dependency') -var CACHE_DIR = path.resolve(PKG_DIR, 'cache') -var DEP_DIR = path.resolve(PKG_DIR, 'dep') - -var desired = { - 'name': 'npm-test-shrinkwrap-local-dependency', - 'version': '0.0.0', - 'dependencies': { - 'npm-test-shrinkwrap-local-dependency-dep': { - 'version': '0.0.0', - 'from': 'dep', - 'resolved': 'file:dep' +var Tacks = require('tacks') +var File = Tacks.File +var Dir = Tacks.Dir + +var testdir = path.resolve(__dirname, path.basename(__filename, '.js')) +var cachedir = path.resolve(testdir, 'cache') +var config = ['--cache=' + cachedir, '--loglevel=error'] + +var shrinkwrap = { + name: 'shrinkwrap-local-dependency', + version: '1.0.0', + dependencies: { + mod2: { + version: '1.0.0', + from: 'mods/mod2', + resolved: 'file:mods/mod2', + dependencies: { + mod1: { + version: '1.0.0', + from: 'mods/mod1', + resolved: 'file:mods/mod1' + } + } } } } -var root = { - 'author': 'Thomas Torp', - 'name': 'npm-test-shrinkwrap-local-dependency', - 'version': '0.0.0', - 'dependencies': { - 'npm-test-shrinkwrap-local-dependency-dep': 'file:./dep' - } +var fixture = new Tacks( + Dir({ + cache: Dir(), + mods: Dir({ + mod1: Dir({ + 'package.json': File({ + name: 'mod1', + version: '1.0.0' + }) + }), + mod2: Dir({ + 'package.json': File({ + name: 'mod2', + version: '1.0.0', + dependencies: { + mod1: 'file:../mod1' + } + }) + }) + }), + 'package.json': File({ + name: 'shrinkwrap-local-dependency', + version: '1.0.0', + dependencies: { + mod2: 'file:mods/mod2' + } + }) + }) +) + +function setup () { + cleanup() + fixture.create(testdir) } -var dependency = { - 'author': 'Thomas Torp', - 'name': 'npm-test-shrinkwrap-local-dependency-dep', - 'version': '0.0.0' +function cleanNodeModules () { + rimraf.sync(path.resolve(testdir, 'node_modules')) +} + +function cleanup () { + fixture.remove(testdir) } test('shrinkwrap uses resolved with file: on local deps', function (t) { setup() - common.npm( - ['--cache=' + CACHE_DIR, '--loglevel=silent', 'install', '.'], - {}, - function (err, code) { - t.ifError(err, 'npm install worked') - t.equal(code, 0, 'npm exited normally') - - common.npm( - ['--cache=' + CACHE_DIR, '--loglevel=silent', 'shrinkwrap'], - {}, - function (err, code) { - t.ifError(err, 'npm shrinkwrap worked') - t.equal(code, 0, 'npm exited normally') + common.npm(config.concat(['install', '--legacy']), {cwd: testdir}, function (err, code, stdout, stderr) { + if (err) throw err + t.comment(stdout.trim()) + t.comment(stderr.trim()) + t.equal(code, 0, 'npm exited normally') - fs.readFile('npm-shrinkwrap.json', { encoding: 'utf8' }, function (err, data) { - t.ifError(err, 'read file correctly') - t.deepEqual(JSON.parse(data), desired, 'shrinkwrap looks correct') - - t.end() - }) - } - ) - } - ) + common.npm(config.concat('shrinkwrap'), {cwd: testdir}, function (err, code, stdout, stderr) { + if (err) throw err + t.comment(stdout.trim()) + t.comment(stderr.trim()) + t.equal(code, 0, 'npm exited normally') + var data = fs.readFileSync(path.join(testdir, 'npm-shrinkwrap.json'), { encoding: 'utf8' }) + t.deepEqual(JSON.parse(data), shrinkwrap, 'shrinkwrap looks correct') + t.end() + }) + }) }) +function exists (file) { + try { + fs.statSync(file) + return true + } catch (ex) { + return false + } +} + test("'npm install' should install local packages from shrinkwrap", function (t) { cleanNodeModules() - common.npm( - ['--cache=' + CACHE_DIR, '--loglevel=silent', 'install', '.'], - {}, - function (err, code) { - t.ifError(err, 'install ran correctly') - t.notOk(code, 'npm install exited with code 0') - var dependencyPackageJson = path.resolve( - PKG_DIR, - 'node_modules/npm-test-shrinkwrap-local-dependency-dep/package.json' - ) - t.ok( - JSON.parse(fs.readFileSync(dependencyPackageJson, 'utf8')), - 'package with local dependency installed from shrinkwrap' - ) - - t.end() - } - ) + common.npm(config.concat(['install']), {cwd: testdir}, function (err, code, stdout, stderr) { + if (err) throw err + t.comment(stdout.trim()) + t.comment(stderr.trim()) + t.equal(code, 0, 'npm exited normally') + t.ok(exists(path.join(testdir, 'node_modules', 'mod2')), 'mod2 exists') + t.ok(exists(path.join(testdir, 'node_modules', 'mod2', 'node_modules', 'mod1')), 'mod1 exists') + t.end() + }) }) test('cleanup', function (t) { @@ -94,28 +122,3 @@ test('cleanup', function (t) { t.end() }) -function setup () { - cleanup() - mkdirp.sync(PKG_DIR) - mkdirp.sync(CACHE_DIR) - mkdirp.sync(DEP_DIR) - fs.writeFileSync( - path.resolve(PKG_DIR, 'package.json'), - JSON.stringify(root, null, 2) - ) - fs.writeFileSync( - path.resolve(DEP_DIR, 'package.json'), - JSON.stringify(dependency, null, 2) - ) - process.chdir(PKG_DIR) -} - -function cleanNodeModules () { - rimraf.sync(path.resolve(PKG_DIR, 'node_modules')) -} - -function cleanup () { - process.chdir(osenv.tmpdir()) - cleanNodeModules() - rimraf.sync(PKG_DIR) -}