diff options
Diffstat (limited to 'deps/npm/lib/install')
-rw-r--r-- | deps/npm/lib/install/action/extract-worker.js | 4 | ||||
-rw-r--r-- | deps/npm/lib/install/action/extract.js | 32 | ||||
-rw-r--r-- | deps/npm/lib/install/actions.js | 2 | ||||
-rw-r--r-- | deps/npm/lib/install/copy-tree.js | 17 | ||||
-rw-r--r-- | deps/npm/lib/install/deps.js | 75 | ||||
-rw-r--r-- | deps/npm/lib/install/diff-trees.js | 3 | ||||
-rw-r--r-- | deps/npm/lib/install/has-modern-meta.js | 19 | ||||
-rw-r--r-- | deps/npm/lib/install/inflate-shrinkwrap.js | 29 | ||||
-rw-r--r-- | deps/npm/lib/install/read-shrinkwrap.js | 66 | ||||
-rw-r--r-- | deps/npm/lib/install/save.js | 7 |
10 files changed, 175 insertions, 79 deletions
diff --git a/deps/npm/lib/install/action/extract-worker.js b/deps/npm/lib/install/action/extract-worker.js index 24508c7804..2b082b4a57 100644 --- a/deps/npm/lib/install/action/extract-worker.js +++ b/deps/npm/lib/install/action/extract-worker.js @@ -10,9 +10,9 @@ module.exports = (args, cb) => { const spec = parsed[0] const extractTo = parsed[1] const opts = parsed[2] - if (!opts.log && opts.loglevel) { + if (!opts.log) { opts.log = npmlog - opts.log.level = opts.loglevel } + opts.log.level = opts.loglevel || opts.log.level BB.resolve(extract(spec, extractTo, opts)).nodeify(cb) } diff --git a/deps/npm/lib/install/action/extract.js b/deps/npm/lib/install/action/extract.js index 6b827f36ea..e8d7a6c4f6 100644 --- a/deps/npm/lib/install/action/extract.js +++ b/deps/npm/lib/install/action/extract.js @@ -4,9 +4,7 @@ const BB = require('bluebird') const stat = BB.promisify(require('graceful-fs').stat) const gentlyRm = BB.promisify(require('../../utils/gently-rm.js')) -const log = require('npmlog') const mkdirp = BB.promisify(require('mkdirp')) -const moduleName = require('../../utils/module-name.js') const moduleStagingPath = require('../module-staging-path.js') const move = require('../../utils/move.js') const npa = require('npm-package-arg') @@ -59,12 +57,11 @@ function extract (staging, pkg, log) { pacoteOpts = require('../../config/pacote') } const opts = pacoteOpts({ - integrity: pkg.package._integrity + integrity: pkg.package._integrity, + resolved: pkg.package._resolved }) const args = [ - pkg.package._resolved - ? npa.resolve(pkg.package.name, pkg.package._resolved) - : pkg.package._requested, + pkg.package._requested, extractTo, opts ] @@ -112,18 +109,6 @@ function readBundled (pkg, staging, extractTo) { }, {concurrency: 10}) } -function getTree (pkg) { - while (pkg.parent) pkg = pkg.parent - return pkg -} - -function warn (pkg, code, msg) { - const tree = getTree(pkg) - const err = new Error(msg) - err.code = code - tree.warnings.push(err) -} - function stageBundledModule (bundler, child, staging, parentPath) { const stageFrom = path.join(parentPath, 'node_modules', child.package.name) const stageTo = moduleStagingPath(staging, child) @@ -146,15 +131,6 @@ function finishModule (bundler, child, stageTo, stageFrom) { return move(stageFrom, stageTo) }) } else { - return stat(stageFrom).then(() => { - const bundlerId = packageId(bundler) - if (!getTree(bundler).warnings.some((w) => { - return w.code === 'EBUNDLEOVERRIDE' - })) { - warn(bundler, 'EBUNDLEOVERRIDE', `${bundlerId} had bundled packages that do not match the required version(s). They have been replaced with non-bundled versions.`) - } - log.verbose('bundle', `EBUNDLEOVERRIDE: Replacing ${bundlerId}'s bundled version of ${moduleName(child)} with ${packageId(child)}.`) - return gentlyRm(stageFrom) - }, () => {}) + return stat(stageFrom).then(() => gentlyRm(stageFrom), () => {}) } } diff --git a/deps/npm/lib/install/actions.js b/deps/npm/lib/install/actions.js index 9608a943a5..a34d03ffe2 100644 --- a/deps/npm/lib/install/actions.js +++ b/deps/npm/lib/install/actions.js @@ -118,7 +118,7 @@ function doParallel (type, staging, actionsToRun, log, next) { } return acc }, []) - log.silly('doParallel', type + ' ' + actionsToRun.length) + log.silly('doParallel', type + ' ' + acts.length) time(log) if (!acts.length) { return next() } return withInit(actions[type], () => { diff --git a/deps/npm/lib/install/copy-tree.js b/deps/npm/lib/install/copy-tree.js index a5b558cf59..2bf7064f33 100644 --- a/deps/npm/lib/install/copy-tree.js +++ b/deps/npm/lib/install/copy-tree.js @@ -1,27 +1,26 @@ 'use strict' var createNode = require('./node.js').create -module.exports = function (tree, filter) { - return copyTree(tree, {}, filter) +module.exports = function (tree) { + return copyTree(tree, {}) } -function copyTree (tree, cache, filter) { - if (filter && !filter(tree)) { return null } +function copyTree (tree, cache) { if (cache[tree.path]) { return cache[tree.path] } var newTree = cache[tree.path] = createNode(Object.assign({}, tree)) - copyModuleList(newTree, 'children', cache, filter) + copyModuleList(newTree, 'children', cache) newTree.children.forEach(function (child) { child.parent = newTree }) - copyModuleList(newTree, 'requires', cache, filter) - copyModuleList(newTree, 'requiredBy', cache, filter) + copyModuleList(newTree, 'requires', cache) + copyModuleList(newTree, 'requiredBy', cache) return newTree } -function copyModuleList (tree, key, cache, filter) { +function copyModuleList (tree, key, cache) { var newList = [] if (tree[key]) { tree[key].forEach(function (child) { - const copy = copyTree(child, cache, filter) + const copy = copyTree(child, cache) if (copy) { newList.push(copy) } diff --git a/deps/npm/lib/install/deps.js b/deps/npm/lib/install/deps.js index 93c4adffd7..54cc5258fa 100644 --- a/deps/npm/lib/install/deps.js +++ b/deps/npm/lib/install/deps.js @@ -33,6 +33,7 @@ var getSaveType = require('./save.js').getSaveType var unixFormatPath = require('../utils/unix-format-path.js') var isExtraneous = require('./is-extraneous.js') var isRegistry = require('../utils/is-registry.js') +var hasModernMeta = require('./has-modern-meta.js') // The export functions in this module mutate a dependency tree, adding // items to them. @@ -111,7 +112,7 @@ function computeMetadata (tree, seen) { const reqs = tree.swRequires || {} for (let name of Object.keys(deps)) { if (findChild(name, deps[name])) continue - if (findChild(name, reqs[name])) continue + if (name in reqs && findChild(name, reqs[name])) continue tree.missingDeps[name] = deps[name] } if (tree.isTop) { @@ -332,9 +333,21 @@ exports.removeDeps = function (args, tree, saveToDependencies, next) { parent.requires = parent.requires.filter((child) => child !== pkgToRemove) } pkgToRemove.requiredBy = pkgToRemove.requiredBy.filter((parent) => parent !== tree) + flagAsRemoving(pkgToRemove) } next() } + +function flagAsRemoving (toRemove, seen) { + if (!seen) seen = new Set() + if (seen.has(toRemove)) return + seen.add(toRemove) + toRemove.removing = true + toRemove.requires.forEach((required) => { + flagAsRemoving(required, seen) + }) +} + exports.removeExtraneous = function (args, tree, next) { for (let pkg of args) { var pkgName = moduleName(pkg) @@ -526,7 +539,7 @@ function addDependency (name, versionSpec, tree, log, done) { } var child = findRequirement(tree, name, req) if (!child && swReq) child = findRequirement(tree, name, swReq) - if (child) { + if (hasModernMeta(child)) { resolveWithExistingModule(child, tree) if (child.package._shrinkwrap === undefined) { readShrinkwrap.andInflate(child, function (er) { next(er, child, log) }) @@ -534,12 +547,42 @@ function addDependency (name, versionSpec, tree, log, done) { next(null, child, log) } } else { + if (child) { + if (req.registry) { + req = childDependencySpecifier(tree, name, child.package.version) + } + if (child.fromBundle) reportBundleOverride(child, log) + removeObsoleteDep(child, log) + } fetchPackageMetadata(req, packageRelativePath(tree), {tracker: log.newItem('fetchMetadata')}, iferr(next, function (pkg) { resolveWithNewModule(pkg, tree, log, next) })) } } +function getTop (pkg) { + const seen = new Set() + while (pkg.parent && !seen.has(pkg.parent)) { + pkg = pkg.parent + seen.add(pkg) + } + return pkg +} + +function reportBundleOverride (child, log) { + const code = 'EBUNDLEOVERRIDE' + const top = getTop(child.fromBundle) + const bundlerId = packageId(child.fromBundle) + if (!top.warnings.some((w) => { + return w.code === code + })) { + const err = new Error(`${bundlerId} had bundled packages that do not match the required version(s). They have been replaced with non-bundled versions.`) + err.code = code + top.warnings.push(err) + } + if (log) log.verbose('bundle', `${code}: Replacing ${bundlerId}'s bundled version of ${moduleName(child)} with ${packageId(child)}.`) +} + function resolveWithExistingModule (child, tree) { validate('OO', arguments) addRequiredDep(tree, child) @@ -592,7 +635,7 @@ function resolveWithNewModule (pkg, tree, log, next) { return isInstallable(pkg, (err) => { let installable = !err addBundled(pkg, (bundleErr) => { - var parent = earliestInstallable(tree, tree, pkg) || tree + var parent = earliestInstallable(tree, tree, pkg, log) || tree var isLink = pkg._requested.type === 'directory' var child = createChild({ package: pkg, @@ -609,7 +652,10 @@ function resolveWithNewModule (pkg, tree, log, next) { var hasBundled = child.children.length var replaced = replaceModuleByName(parent, 'children', child) - if (replaced) removeObsoleteDep(replaced) + if (replaced) { + if (replaced.fromBundle) reportBundleOverride(replaced, log) + removeObsoleteDep(replaced) + } addRequiredDep(tree, child) child.location = flatNameFromTree(child) @@ -694,12 +740,25 @@ function preserveSymlinks () { // Find the highest level in the tree that we can install this module in. // If the module isn't installed above us yet, that'd be the very top. // If it is, then it's the level below where its installed. -var earliestInstallable = exports.earliestInstallable = function (requiredBy, tree, pkg) { - validate('OOO', arguments) +var earliestInstallable = exports.earliestInstallable = function (requiredBy, tree, pkg, log) { + validate('OOOO', arguments) + function undeletedModuleMatches (child) { return !child.removed && moduleName(child) === pkg.name } - if (tree.children.some(undeletedModuleMatches)) return null + const undeletedMatches = tree.children.filter(undeletedModuleMatches) + if (undeletedMatches.length) { + // if there's a conflict with another child AT THE SAME level then we're replacing it, so + // mark it as removed and continue with resolution normally. + if (tree === requiredBy) { + undeletedMatches.forEach((pkg) => { + if (pkg.fromBundle) reportBundleOverride(pkg, log) + removeObsoleteDep(pkg, log) + }) + } else { + return null + } + } // If any of the children of this tree have conflicting // binaries then we need to decline to install this package here. @@ -738,5 +797,5 @@ var earliestInstallable = exports.earliestInstallable = function (requiredBy, tr if (!preserveSymlinks() && /^[.][.][\\/]/.test(path.relative(tree.parent.realpath, tree.realpath))) return tree - return (earliestInstallable(requiredBy, tree.parent, pkg) || tree) + return (earliestInstallable(requiredBy, tree.parent, pkg, log) || tree) } diff --git a/deps/npm/lib/install/diff-trees.js b/deps/npm/lib/install/diff-trees.js index 4316f351cc..06e6b77a91 100644 --- a/deps/npm/lib/install/diff-trees.js +++ b/deps/npm/lib/install/diff-trees.js @@ -70,6 +70,9 @@ function sriMatch (aa, bb) { function pkgAreEquiv (aa, bb) { // coming in we know they share a path… + // if one is inside a link and the other is not, then they are not equivalent + // this happens when we're replacing a linked dep with a non-linked version + if (aa.isInLink !== bb.isInLink) return false // if they share package metadata _identity_, they're the same thing if (aa.package === bb.package) return true // if they share integrity information, they're the same thing diff --git a/deps/npm/lib/install/has-modern-meta.js b/deps/npm/lib/install/has-modern-meta.js new file mode 100644 index 0000000000..382e74c70c --- /dev/null +++ b/deps/npm/lib/install/has-modern-meta.js @@ -0,0 +1,19 @@ +'use strict' +module.exports = hasModernMeta + +const npa = require('npm-package-arg') +const moduleName = require('../utils/module-name.js') + +function isLink (child) { + return child.isLink || (child.parent && isLink(child.parent)) +} + +function hasModernMeta (child) { + if (!child) return false + const resolved = child.package._resolved && npa.resolve(moduleName(child), child.package._resolved) + return child.isTop || isLink(child) || ( + child.package && + resolved && + (child.package._integrity || child.package._shasum || resolved.type === 'git') + ) +} diff --git a/deps/npm/lib/install/inflate-shrinkwrap.js b/deps/npm/lib/install/inflate-shrinkwrap.js index 43ac9136f0..e596ca94ab 100644 --- a/deps/npm/lib/install/inflate-shrinkwrap.js +++ b/deps/npm/lib/install/inflate-shrinkwrap.js @@ -14,6 +14,8 @@ const realizeShrinkwrapSpecifier = require('./realize-shrinkwrap-specifier.js') const validate = require('aproba') const path = require('path') const isRegistry = require('../utils/is-registry.js') +const hasModernMeta = require('./has-modern-meta.js') +const ssri = require('ssri') module.exports = function (tree, sw, opts, finishInflating) { if (!fetchPackageMetadata) { @@ -68,7 +70,7 @@ function normalizePackageDataNoErrors (pkg) { function inflatableChild (onDiskChild, name, topPath, tree, sw, requested, opts) { validate('OSSOOOO|ZSSOOOO', arguments) - if (onDiskChild && childIsEquivalent(sw, requested, onDiskChild)) { + if (hasModernMeta(onDiskChild) && childIsEquivalent(sw, requested, onDiskChild)) { // The version on disk matches the shrinkwrap entry. if (!onDiskChild.fromShrinkwrap) onDiskChild.fromShrinkwrap = true onDiskChild.package._requested = requested @@ -106,7 +108,7 @@ function makeFakeChild (name, topPath, tree, sw, requested) { name: name, version: sw.version, _id: name + '@' + sw.version, - _resolved: adaptResolved(requested, sw.resolved), + _resolved: sw.resolved, _requested: requested, _optional: sw.optional, _development: sw.dev, @@ -144,23 +146,6 @@ function makeFakeChild (name, topPath, tree, sw, requested) { return child } -function adaptResolved (requested, resolved) { - const registry = requested.scope - ? npm.config.get(`${requested.scope}:registry`) || npm.config.get('registry') - : npm.config.get('registry') - if (!isRegistry(requested) || (resolved && resolved.indexOf(registry) === 0)) { - // Nothing to worry about here. Pass it through. - return resolved - } else { - // We could fast-path for registry.npmjs.org here, but if we do, it - // would end up getting written back to the `resolved` field. By always - // returning `null` for other registries, `pacote.extract()` will take - // care of any required metadata fetches internally, without altering - // the tree we're going to write out to shrinkwrap/lockfile. - return null - } -} - function fetchChild (topPath, tree, sw, requested) { return fetchPackageMetadata(requested, topPath).then((pkg) => { pkg._from = sw.from || requested.raw @@ -196,7 +181,11 @@ function fetchChild (topPath, tree, sw, requested) { function childIsEquivalent (sw, requested, child) { if (!child) return false if (child.fromShrinkwrap) return true - if (sw.integrity && child.package._integrity === sw.integrity) return true + if ( + sw.integrity && + child.package._integrity && + ssri.parse(sw.integrity).match(child.package._integrity) + ) return true if (child.isLink && requested.type === 'directory') return path.relative(child.realpath, requested.fetchSpec) === '' if (sw.resolved) return child.package._resolved === sw.resolved diff --git a/deps/npm/lib/install/read-shrinkwrap.js b/deps/npm/lib/install/read-shrinkwrap.js index 45e883caa2..a48a4aea00 100644 --- a/deps/npm/lib/install/read-shrinkwrap.js +++ b/deps/npm/lib/install/read-shrinkwrap.js @@ -25,14 +25,7 @@ function readShrinkwrap (child, next) { log.warn('read-shrinkwrap', 'Ignoring package-lock.json because there is already an npm-shrinkwrap.json. Please use only one of the two.') } const name = shrinkwrap ? 'npm-shrinkwrap.json' : 'package-lock.json' - let parsed = null - if (shrinkwrap || lockfile) { - try { - parsed = parseJSON(shrinkwrap || lockfile) - } catch (ex) { - throw ex - } - } + const parsed = parsePkgLock(shrinkwrap || lockfile, name) if (parsed && parsed.lockfileVersion !== PKGLOCK_VERSION) { log.warn('read-shrinkwrap', `This version of npm is compatible with lockfileVersion@${PKGLOCK_VERSION}, but ${name} was generated for lockfileVersion@${parsed.lockfileVersion || 0}. I'll try to do my best with it!`) } @@ -43,7 +36,8 @@ function readShrinkwrap (child, next) { function maybeReadFile (name, child) { return readFileAsync( - path.join(child.path, name) + path.join(child.path, name), + 'utf8' ).catch({code: 'ENOENT'}, () => null) } @@ -56,3 +50,57 @@ module.exports.andInflate = function (child, next) { } })) } + +const PARENT_RE = /\|{7,}/g +const OURS_RE = /\<{7,}/g +const THEIRS_RE = /\={7,}/g +const END_RE = /\>{7,}/g + +module.exports._isDiff = isDiff +function isDiff (str) { + return str.match(OURS_RE) && str.match(THEIRS_RE) && str.match(END_RE) +} + +module.exports._parsePkgLock = parsePkgLock +function parsePkgLock (str, filename) { + if (!str) { return null } + try { + return parseJSON(str) + } catch (e) { + if (isDiff(str)) { + log.warn('conflict', `A git conflict was detected in ${filename}. Attempting to auto-resolve.`) + const pieces = str.split(/[\n\r]+/g).reduce((acc, line) => { + if (line.match(PARENT_RE)) acc.state = 'parent' + else if (line.match(OURS_RE)) acc.state = 'ours' + else if (line.match(THEIRS_RE)) acc.state = 'theirs' + else if (line.match(END_RE)) acc.state = 'top' + else { + if (acc.state === 'top' || acc.state === 'ours') acc.ours += line + if (acc.state === 'top' || acc.state === 'theirs') acc.theirs += line + if (acc.state === 'top' || acc.state === 'parent') acc.parent += line + } + return acc + }, { + state: 'top', + ours: '', + theirs: '', + parent: '' + }) + try { + const ours = parseJSON(pieces.ours) + const theirs = parseJSON(pieces.theirs) + return reconcileLockfiles(ours, theirs) + } catch (_e) { + log.error('conflict', `Automatic conflict resolution failed. Please manually resolve conflicts in ${filename} and try again.`) + log.silly('conflict', `Error during resolution: ${_e}`) + throw e + } + } else { + throw e + } + } +} + +function reconcileLockfiles (parent, ours, theirs) { + return Object.assign({}, ours, theirs) +} diff --git a/deps/npm/lib/install/save.js b/deps/npm/lib/install/save.js index f0c61f555d..3f62643b80 100644 --- a/deps/npm/lib/install/save.js +++ b/deps/npm/lib/install/save.js @@ -3,6 +3,7 @@ const createShrinkwrap = require('../shrinkwrap.js').createShrinkwrap const deepSortObject = require('../utils/deep-sort-object.js') const detectIndent = require('detect-indent') +const detectNewline = require('detect-newline') const fs = require('graceful-fs') const iferr = require('iferr') const log = require('npmlog') @@ -10,6 +11,7 @@ const moduleName = require('../utils/module-name.js') const npm = require('../npm.js') const parseJSON = require('../utils/parse-json.js') const path = require('path') +const stringifyPackage = require('../utils/stringify-package') const validate = require('aproba') const without = require('lodash.without') const writeFileAtomic = require('write-file-atomic') @@ -60,7 +62,8 @@ function savePackageJson (tree, next) { // don't use readJson, because we don't want to do all the other // tricky npm-specific stuff that's in there. fs.readFile(saveTarget, 'utf8', iferr(next, function (packagejson) { - const indent = detectIndent(packagejson).indent || ' ' + const indent = detectIndent(packagejson).indent + const newline = detectNewline(packagejson) try { tree.package = parseJSON(packagejson) } catch (ex) { @@ -122,7 +125,7 @@ function savePackageJson (tree, next) { tree.package.bundleDependencies = deepSortObject(bundle) } - var json = JSON.stringify(tree.package, null, indent) + '\n' + var json = stringifyPackage(tree.package, indent, newline) if (json === packagejson) { log.verbose('shrinkwrap', 'skipping write for package.json because there were no changes.') next() |