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

Commit

Permalink
Merge 8334e9e into 27dd0c2
Browse files Browse the repository at this point in the history
  • Loading branch information
zkat committed Feb 13, 2018
2 parents 27dd0c2 + 8334e9e commit 1805993
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 8 deletions.
52 changes: 46 additions & 6 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const fs = require('graceful-fs')
const getPrefix = require('find-npm-prefix')
const lifecycle = require('npm-lifecycle')
const lockVerify = require('lock-verify')
const mkdirp = BB.promisify(require('mkdirp'))
const npa = require('npm-package-arg')
const path = require('path')
const readPkgJson = BB.promisify(require('read-package-json'))
Expand All @@ -17,6 +18,7 @@ const rimraf = BB.promisify(require('rimraf'))
const appendFileAsync = BB.promisify(fs.appendFile)
const readFileAsync = BB.promisify(fs.readFile)
const statAsync = BB.promisify(fs.stat)
const symlinkAsync = BB.promisify(fs.symlink)
const truncateAsync = BB.promisify(fs.truncate)

class Installer {
Expand Down Expand Up @@ -132,12 +134,50 @@ class Installer {
if (dep.dev && this.config.get('production')) { return }
const depPath = dep.path(this.prefix)
// Process children first, then extract this child
!dep.isRoot && this.log.silly('extractTree', `${dep.name} -> ${depPath}`)
return BB.join(
!dep.isRoot &&
extract.child(dep.name, dep, depPath, this.config, this.opts),
next()
).then(() => { !dep.isRoot && this.pkgCount++ })
const spec = npa.resolve(dep.name, dep.version, this.prefix)
if (dep.isRoot) {
return next()
} else if (spec.type === 'directory') {
const relative = path.relative(path.dirname(depPath), spec.fetchSpec)
this.log.silly('extractTree', `${dep.name}@${spec.fetchSpec} -> ${depPath} (symlink)`)
return mkdirp(path.dirname(depPath))
.then(() => symlinkAsync(relative, depPath, 'junction'))
.catch(
() => rimraf(depPath)
.then(() => symlinkAsync(relative, depPath, 'junction'))
).then(() => next())
.then(() => { this.pkgCount++ })
} else {
this.log.silly('extractTree', `${dep.name}@${dep.version} -> ${depPath}`)
return (
dep.bundled
? statAsync(depPath).catch(err => {
if (err.code !== 'ENOENT') { throw err }
})
: BB.resolve(false)
)
.then(wasBundled => {
const hasBundled = Array.from(dep.dependencies.values())
.some(d => d.bundled)

if (hasBundled) {
return BB.resolve(
wasBundled ||
extract.child(dep.name, dep, depPath, this.config, this.opts)
)
.then(next)
} else {
// If it has no bundled children, we can extract children
// concurrently
return BB.join(
wasBundled ||
extract.child(dep.name, dep, depPath, this.config, this.opts),
next()
)
}
})
.then(() => { this.pkgCount++ })
}
}, {concurrency: 50, Promise: BB})
}

Expand Down
2 changes: 0 additions & 2 deletions lib/extract.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ module.exports = {
},

child (name, child, childPath, config, opts) {
if (child.bundled) return BB.resolve()

const spec = npa.resolve(name, child.version)
const childOpts = config.toPacote(Object.assign({
integrity: child.integrity,
Expand Down
43 changes: 43 additions & 0 deletions test/specs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,49 @@ test('handles dependency list with only deep subdeps', t => {
})
})

test('installs `directory` dependencies as symlinks', t => {
const fixture = new Tacks(Dir({
'package.json': File({
name: pkgName,
version: pkgVersion,
dependencies: {
'a': 'file:a'
}
}),
'package-lock.json': File({
name: pkgName,
verson: pkgVersion,
dependencies: {
a: {
version: 'file:a'
}
},
lockfileVersion: 1
}),
'a': Dir({
'package.json': File({
name: 'a',
version: '1.2.3'
}),
'index.js': File('"hello"')
})
}))
fixture.create(prefix)

const modPath = path.join(prefix, 'node_modules', 'a')
return run().then(details => {
t.equal(details.pkgCount, 1)
return fs.lstatAsync(modPath)
})
.then(stat => t.ok(stat.isSymbolicLink(), '`a` is a symlink'))

.then(() => fs.realpathAsync(modPath))
.then(realpath => t.equal(realpath, path.join(prefix, 'a'), 'realpath ok'))

.then(() => fs.readFileAsync(path.join(modPath, 'index.js'), 'utf8'))
.then(data => t.equal(data, '"hello"', 'extracted data matches'))
})

test('prioritizes npm-shrinkwrap over package-lock if both present', t => {
const fixture = new Tacks(Dir({
'package.json': File({
Expand Down

0 comments on commit 1805993

Please sign in to comment.