summaryrefslogtreecommitdiff
path: root/deps/npm/lib/shrinkwrap.js
diff options
context:
space:
mode:
Diffstat (limited to 'deps/npm/lib/shrinkwrap.js')
-rw-r--r--deps/npm/lib/shrinkwrap.js264
1 files changed, 178 insertions, 86 deletions
diff --git a/deps/npm/lib/shrinkwrap.js b/deps/npm/lib/shrinkwrap.js
index 5e12f0bd81..75fe0dd95d 100644
--- a/deps/npm/lib/shrinkwrap.js
+++ b/deps/npm/lib/shrinkwrap.js
@@ -1,30 +1,39 @@
-// emit JSON describing versions of all packages currently installed (for later
-// use with shrinkwrap install)
+'use strict'
-module.exports = exports = shrinkwrap
+const BB = require('bluebird')
-var path = require('path')
-var log = require('npmlog')
-var writeFileAtomic = require('write-file-atomic')
-var iferr = require('iferr')
-var readPackageJson = require('read-package-json')
-var readPackageTree = require('read-package-tree')
-var validate = require('aproba')
-var chain = require('slide').chain
-var npm = require('./npm.js')
-var recalculateMetadata = require('./install/deps.js').recalculateMetadata
-var validatePeerDeps = require('./install/deps.js').validatePeerDeps
-var isExtraneous = require('./install/is-extraneous.js')
-var packageId = require('./utils/package-id.js')
-var moduleName = require('./utils/module-name.js')
-var output = require('./utils/output.js')
-var lifecycle = require('./utils/lifecycle.js')
-var isDevDep = require('./install/is-dev-dep.js')
-var isProdDep = require('./install/is-prod-dep.js')
-var isOptDep = require('./install/is-opt-dep.js')
+const chain = require('slide').chain
+const detectIndent = require('detect-indent')
+const fs = BB.promisifyAll(require('graceful-fs'))
+const getRequested = require('./install/get-requested.js')
+const id = require('./install/deps.js')
+const iferr = require('iferr')
+const isDevDep = require('./install/is-dev-dep.js')
+const isExtraneous = require('./install/is-extraneous.js')
+const isOptDep = require('./install/is-opt-dep.js')
+const isProdDep = require('./install/is-prod-dep.js')
+const lifecycle = require('./utils/lifecycle.js')
+const log = require('npmlog')
+const moduleName = require('./utils/module-name.js')
+const move = require('move-concurrently')
+const npm = require('./npm.js')
+const packageId = require('./utils/package-id.js')
+const path = require('path')
+const pkgSri = require('./utils/package-integrity.js')
+const readPackageTree = BB.promisify(require('read-package-tree'))
+const ssri = require('ssri')
+const validate = require('aproba')
+const writeFileAtomic = require('write-file-atomic')
+const PKGLOCK = 'package-lock.json'
+const SHRINKWRAP = 'npm-shrinkwrap.json'
+const PKGLOCK_VERSION = npm.lockfileVersion
+
+// emit JSON describing versions of all packages currently installed (for later
+// use with shrinkwrap install)
shrinkwrap.usage = 'npm shrinkwrap'
+module.exports = exports = shrinkwrap
function shrinkwrap (args, silent, cb) {
if (typeof cb !== 'function') {
cb = silent
@@ -35,56 +44,64 @@ function shrinkwrap (args, silent, cb) {
log.warn('shrinkwrap', "doesn't take positional args")
}
- var packagePath = path.join(npm.localPrefix, 'package.json')
- var prod = npm.config.get('production') || /^prod/.test(npm.config.get('only'))
-
- readPackageJson(packagePath, iferr(cb, function (pkg) {
- createShrinkwrap(npm.localPrefix, pkg, !prod, silent, cb)
- }))
+ move(
+ path.resolve(npm.prefix, PKGLOCK),
+ path.resolve(npm.prefix, SHRINKWRAP),
+ { Promise: BB }
+ ).then(() => {
+ log.notice('', `${PKGLOCK} has been renamed to ${SHRINKWRAP}. ${SHRINKWRAP} will be used for future installations.`)
+ return fs.readFileAsync(path.resolve(npm.prefix, SHRINKWRAP)).then((d) => {
+ return JSON.parse(d)
+ })
+ }, (err) => {
+ if (err.code !== 'ENOENT') {
+ throw err
+ } else {
+ return readPackageTree(npm.localPrefix).then(
+ id.computeMetadata
+ ).then((tree) => {
+ return BB.fromNode((cb) => {
+ createShrinkwrap(tree, {
+ silent,
+ defaultFile: SHRINKWRAP
+ }, cb)
+ })
+ })
+ }
+ }).then((data) => cb(null, data), cb)
}
module.exports.createShrinkwrap = createShrinkwrap
-function createShrinkwrap (dir, pkg, dev, silent, cb) {
- lifecycle(pkg, 'preshrinkwrap', dir, function () {
- readPackageTree(dir, andRecalculateMetadata(iferr(cb, function (tree) {
- var pkginfo = treeToShrinkwrap(tree, dev)
-
- chain([
- [lifecycle, tree.package, 'shrinkwrap', dir],
- [shrinkwrap_, pkginfo, silent],
- [lifecycle, tree.package, 'postshrinkwrap', dir]
- ], iferr(cb, function (data) {
- cb(null, data[0])
- }))
- })))
+function createShrinkwrap (tree, opts, cb) {
+ opts = opts || {}
+ lifecycle(tree.package, 'preshrinkwrap', tree.path, function () {
+ const pkginfo = treeToShrinkwrap(tree)
+ chain([
+ [lifecycle, tree.package, 'shrinkwrap', tree.path],
+ [shrinkwrap_, tree.path, pkginfo, opts],
+ [lifecycle, tree.package, 'postshrinkwrap', tree.path]
+ ], iferr(cb, function (data) {
+ cb(null, pkginfo)
+ }))
})
}
-function andRecalculateMetadata (next) {
- validate('F', arguments)
- return function (er, tree) {
- validate('EO', arguments)
- if (er) return next(er)
- recalculateMetadata(tree, log, next)
- }
-}
-
-function treeToShrinkwrap (tree, dev) {
- validate('OB', arguments)
+function treeToShrinkwrap (tree) {
+ validate('O', arguments)
var pkginfo = {}
if (tree.package.name) pkginfo.name = tree.package.name
if (tree.package.version) pkginfo.version = tree.package.version
var problems = []
if (tree.children.length) {
- shrinkwrapDeps(dev, problems, pkginfo.dependencies = {}, tree)
+ shrinkwrapDeps(problems, pkginfo.dependencies = {}, tree, tree)
}
if (problems.length) pkginfo.problems = problems
return pkginfo
}
-function shrinkwrapDeps (dev, problems, deps, tree, seen) {
- validate('BAOO', [dev, problems, deps, tree])
+function shrinkwrapDeps (problems, deps, top, tree, seen) {
+ validate('AOOO', [problems, deps, top, tree])
if (!seen) seen = {}
if (seen[tree.path]) return
seen[tree.path] = true
@@ -100,58 +117,133 @@ function shrinkwrapDeps (dev, problems, deps, tree, seen) {
})
tree.children.sort(function (aa, bb) { return moduleName(aa).localeCompare(moduleName(bb)) }).forEach(function (child) {
var childIsOnlyDev = isOnlyDev(child)
- if (!dev && childIsOnlyDev) {
- log.warn('shrinkwrap', 'Excluding devDependency: %s', child.location)
+ if (child.package._injectedFromShrinkwrap) {
+ deps[moduleName(child)] = child.package._injectedFromShrinkwrap
return
}
var pkginfo = deps[moduleName(child)] = {}
- pkginfo.version = child.package.version
- pkginfo.from = child.package._from
- pkginfo.resolved = child.package._resolved
- if (dev && childIsOnlyDev) pkginfo.dev = true
+ var req = child.package._requested || getRequested(child)
+ if (req.type === 'directory' || req.type === 'file') {
+ pkginfo.version = 'file:' + path.relative(top.path, child.package._resolved || req.fetchSpec)
+ } else if (!req.registry && !child.fromBundle) {
+ pkginfo.version = child.package._resolved || req.saveSpec || req.rawSpec
+ } else {
+ pkginfo.version = child.package.version
+ }
+ if (child.fromBundle || child.isInLink) {
+ pkginfo.bundled = true
+ } else {
+ if (req.registry) {
+ pkginfo.resolved = child.package._resolved
+ }
+ // no integrity for git deps as integirty hashes are based on the
+ // tarball and we can't (yet) create consistent tarballs from a stable
+ // source.
+ if (req.type !== 'git') {
+ pkginfo.integrity = child.package._integrity
+ if (!pkginfo.integrity && child.package._shasum) {
+ pkginfo.integrity = ssri.fromHex(child.package._shasum, 'sha1')
+ }
+ }
+ }
+ if (childIsOnlyDev) pkginfo.dev = true
if (isOptional(child)) pkginfo.optional = true
if (isExtraneous(child)) {
problems.push('extraneous: ' + child.package._id + ' ' + child.path)
}
- validatePeerDeps(child, function (tree, pkgname, version) {
+ id.validatePeerDeps(child, function (tree, pkgname, version) {
problems.push('peer invalid: ' + pkgname + '@' + version +
', required by ' + child.package._id)
})
if (child.children.length) {
- shrinkwrapDeps(dev, problems, pkginfo.dependencies = {}, child, seen)
+ pkginfo.dependencies = {}
+ shrinkwrapDeps(problems, pkginfo.dependencies, top, child, seen)
}
})
}
-function shrinkwrap_ (pkginfo, silent, cb) {
- if (pkginfo.problems) {
- return cb(new Error('Problems were encountered\n' +
- 'Please correct and try again.\n' +
- pkginfo.problems.join('\n')))
- }
-
- save(pkginfo, silent, cb)
+function shrinkwrap_ (dir, pkginfo, opts, cb) {
+ save(dir, pkginfo, opts, cb)
}
-function save (pkginfo, silent, cb) {
+function save (dir, pkginfo, opts, cb) {
// copy the keys over in a well defined order
// because javascript objects serialize arbitrarily
- var swdata
- try {
- swdata = JSON.stringify(pkginfo, null, 2) + '\n'
- } catch (er) {
- log.error('shrinkwrap', 'Error converting package info to json')
- return cb(er)
- }
-
- var file = path.resolve(npm.prefix, 'npm-shrinkwrap.json')
+ BB.join(
+ checkPackageFile(dir, SHRINKWRAP),
+ checkPackageFile(dir, PKGLOCK),
+ checkPackageFile(dir, 'package.json'),
+ (shrinkwrap, lockfile, pkg) => {
+ const info = (
+ shrinkwrap ||
+ lockfile ||
+ {
+ path: path.resolve(dir, opts.defaultFile || PKGLOCK),
+ data: '{}',
+ indent: (pkg && pkg.indent) || 2
+ }
+ )
+ const updated = updateLockfileMetadata(pkginfo, pkg && pkg.data)
+ const swdata = JSON.stringify(updated, null, info.indent) + '\n'
+ writeFileAtomic(info.path, swdata, (err) => {
+ if (err) return cb(err)
+ if (opts.silent) return cb(null, pkginfo)
+ if (!shrinkwrap && !lockfile) {
+ log.notice('', `created a lockfile as ${path.basename(info.path)}. You should commit this file.`)
+ }
+ cb(null, pkginfo)
+ })
+ }
+ ).then((file) => {
+ }, cb)
+}
- writeFileAtomic(file, swdata, function (er) {
- if (er) return cb(er)
- if (silent) return cb(null, pkginfo)
- output('wrote npm-shrinkwrap.json')
- cb(null, pkginfo)
+function updateLockfileMetadata (pkginfo, pkgJson) {
+ // This is a lot of work just to make sure the extra metadata fields are
+ // between version and dependencies fields, without affecting any other stuff
+ const newPkg = {}
+ let metainfoWritten = false
+ const metainfo = new Set([
+ 'lockfileVersion',
+ 'packageIntegrity',
+ 'preserveSymlinks'
+ ])
+ Object.keys(pkginfo).forEach((k) => {
+ if (k === 'dependencies') {
+ writeMetainfo(newPkg)
+ }
+ if (!metainfo.has(k)) {
+ newPkg[k] = pkginfo[k]
+ }
+ if (k === 'version') {
+ writeMetainfo(newPkg)
+ }
})
+ if (!metainfoWritten) {
+ writeMetainfo(newPkg)
+ }
+ function writeMetainfo (pkginfo) {
+ pkginfo.lockfileVersion = PKGLOCK_VERSION
+ pkginfo.packageIntegrity = pkgJson && pkgSri.hash(pkgJson)
+ if (process.env.NODE_PRESERVE_SYMLINKS) {
+ pkginfo.preserveSymlinks = process.env.NODE_PRESERVE_SYMLINKS
+ }
+ metainfoWritten = true
+ }
+ return newPkg
+}
+
+function checkPackageFile (dir, name) {
+ const file = path.resolve(dir, name)
+ return fs.readFileAsync(
+ file, 'utf8'
+ ).then((data) => {
+ return {
+ path: file,
+ data: JSON.parse(data),
+ indent: detectIndent(data).indent || 2
+ }
+ }).catch({code: 'ENOENT'}, () => {})
}
// Returns true if the module `node` is only required direcctly as a dev