Skip to content
This repository has been archived by the owner on Aug 11, 2022. It is now read-only.

Commit

Permalink
install: Resolve local deps in shrinkwrap relative to top
Browse files Browse the repository at this point in the history
Credit: @iarna
PR-URL: #13214
Reviewed-By: @zkat
  • Loading branch information
iarna authored and zkat committed Jun 30, 2016
1 parent afa2133 commit 346bba1
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 108 deletions.
22 changes: 13 additions & 9 deletions 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
Expand All @@ -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) {
Expand All @@ -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)))
}
}
}
Expand Down Expand Up @@ -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)
}
}
}
28 changes: 21 additions & 7 deletions 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)
}
}
187 changes: 95 additions & 92 deletions test/tap/shrinkwrap-local-dependency.js
@@ -1,121 +1,124 @@
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) {
cleanup()
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)
}

0 comments on commit 346bba1

Please sign in to comment.