| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 | 'use strict'const BB = require('bluebird')let addBundledconst childPath = require('../utils/child-path.js')const createChild = require('./node.js').createlet fetchPackageMetadataconst inflateBundled = require('./inflate-bundled.js')const moduleName = require('../utils/module-name.js')const normalizePackageData = require('normalize-package-data')const npm = require('../npm.js')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')const npa = require('npm-package-arg')module.exports = function (tree, sw, opts, finishInflating) {  if (!fetchPackageMetadata) {    fetchPackageMetadata = BB.promisify(require('../fetch-package-metadata.js'))    addBundled = BB.promisify(fetchPackageMetadata.addBundled)  }  if (arguments.length === 3) {    finishInflating = opts    opts = {}  }  if (!npm.config.get('shrinkwrap') || !npm.config.get('package-lock')) {    return finishInflating()  }  tree.loaded = false  tree.hasRequiresFromLock = sw.requires  return inflateShrinkwrap(tree.path, tree, sw.dependencies, opts).then(    () => finishInflating(),    finishInflating  )}function inflateShrinkwrap (topPath, tree, swdeps, opts) {  if (!swdeps) return Promise.resolve()  if (!opts) opts = {}  const onDisk = {}  tree.children.forEach((child) => {    onDisk[moduleName(child)] = child  })  tree.children = []  return BB.each(Object.keys(swdeps), (name) => {    const sw = swdeps[name]    const dependencies = sw.dependencies || {}    const requested = realizeShrinkwrapSpecifier(name, sw, topPath)    if (Object.keys(sw).length === 0) {      let message = `Object for dependency "${name}" is empty.\n`      message += 'Something went wrong. Regenerate the package-lock.json with "npm install".\n'      message += 'If using a shrinkwrap, regenerate with "npm shrinkwrap".'      return Promise.reject(new Error(message))    }    return inflatableChild(      onDisk[name], name, topPath, tree, sw, requested, opts    ).then((child) => {      child.hasRequiresFromLock = tree.hasRequiresFromLock      return inflateShrinkwrap(topPath, child, dependencies)    })  })}function normalizePackageDataNoErrors (pkg) {  try {    normalizePackageData(pkg)  } catch (ex) {    // don't care  }}function quotemeta (str) {  return str.replace(/([^A-Za-z_0-9/])/g, '\\$1')}function tarballToVersion (name, tb) {  const registry = quotemeta(npm.config.get('registry') || '')    .replace(/https?:/, 'https?:')    .replace(/([^/])$/, '$1/')  let matchRegTarball  if (name) {    const nameMatch = quotemeta(name)    matchRegTarball = new RegExp(`^${registry}${nameMatch}/-/${nameMatch}-(.*)[.]tgz$`)  } else {    matchRegTarball = new RegExp(`^${registry}(.*)?/-/\\1-(.*)[.]tgz$`)  }  const match = tb.match(matchRegTarball)  if (!match) return  return match[2] || match[1]}function relativizeLink (name, spec, topPath, requested) {  if (!spec.startsWith('file:')) {    return  }  let requestedPath = requested.fetchSpec  if (requested.type === 'file') {    requestedPath = path.dirname(requestedPath)  }  const relativized = path.relative(requestedPath, path.resolve(topPath, spec.slice(5)))  return 'file:' + relativized}function inflatableChild (onDiskChild, name, topPath, tree, sw, requested, opts) {  validate('OSSOOOO|ZSSOOOO', arguments)  const usesIntegrity = (    requested.registry ||    requested.type === 'remote' ||    requested.type === 'file'  )  const regTarball = tarballToVersion(name, sw.version)  if (regTarball) {    sw.resolved = sw.version    sw.version = regTarball  }  if (sw.requires) {    Object.keys(sw.requires).forEach(name => {      const spec = sw.requires[name]      sw.requires[name] = tarballToVersion(name, spec) ||        relativizeLink(name, spec, topPath, requested) ||        spec    })  }  const modernLink = requested.type === 'directory' && !sw.from  if (hasModernMeta(onDiskChild) && childIsEquivalent(sw, requested, onDiskChild)) {    // The version on disk matches the shrinkwrap entry.    if (!onDiskChild.fromShrinkwrap) onDiskChild.fromShrinkwrap = requested    onDiskChild.package._requested = requested    onDiskChild.package._spec = requested.rawSpec    onDiskChild.package._where = topPath    onDiskChild.package._optional = sw.optional    onDiskChild.package._development = sw.dev    onDiskChild.package._inBundle = sw.bundled    onDiskChild.fromBundle = (sw.bundled || onDiskChild.package._inBundle) ? tree.fromBundle || tree : null    if (!onDiskChild.package._args) onDiskChild.package._args = []    onDiskChild.package._args.push([String(requested), topPath])    // non-npm registries can and will return unnormalized data, plus    // even the npm registry may have package data normalized with older    // normalization rules. This ensures we get package data in a consistent,    // stable format.    normalizePackageDataNoErrors(onDiskChild.package)    onDiskChild.swRequires = sw.requires    tree.children.push(onDiskChild)    return BB.resolve(onDiskChild)  } else if ((sw.version && (sw.integrity || !usesIntegrity) && (requested.type !== 'directory' || modernLink)) || sw.bundled) {    // The shrinkwrap entry has an integrity field. We can fake a pkg to get    // the installer to do a content-address fetch from the cache, if possible.    return BB.resolve(makeFakeChild(name, topPath, tree, sw, requested))  } else {    // It's not on disk, and we can't just look it up by address -- do a full    // fpm/inflate bundle pass. For registry deps, this will go straight to the    // tarball URL, as if it were a remote tarball dep.    return fetchChild(topPath, tree, sw, requested)  }}function isGit (sw) {  const version = npa.resolve(sw.name, sw.version)  return (version && version.type === 'git')}function makeFakeChild (name, topPath, tree, sw, requested) {  const isDirectory = requested.type === 'directory'  const from = sw.from || requested.raw  const pkg = {    name: name,    version: sw.version,    _id: name + '@' + sw.version,    _resolved: sw.resolved || (isGit(sw) && sw.version),    _requested: requested,    _optional: sw.optional,    _development: sw.dev,    _inBundle: sw.bundled,    _integrity: sw.integrity,    _from: from,    _spec: requested.rawSpec,    _where: topPath,    _args: [[requested.toString(), topPath]],    dependencies: sw.requires  }  if (!sw.bundled) {    const bundleDependencies = Object.keys(sw.dependencies || {}).filter((d) => sw.dependencies[d].bundled)    if (bundleDependencies.length === 0) {      pkg.bundleDependencies = bundleDependencies    }  }  const child = createChild({    package: pkg,    loaded: isDirectory,    parent: tree,    children: [],    fromShrinkwrap: requested,    fakeChild: sw,    fromBundle: sw.bundled ? tree.fromBundle || tree : null,    path: childPath(tree.path, pkg),    realpath: isDirectory ? requested.fetchSpec : childPath(tree.realpath, pkg),    location: (tree.location === '/' ? '' : tree.location + '/') + pkg.name,    isLink: isDirectory,    isInLink: tree.isLink || tree.isInLink,    swRequires: sw.requires  })  tree.children.push(child)  return child}function fetchChild (topPath, tree, sw, requested) {  return fetchPackageMetadata(requested, topPath).then((pkg) => {    pkg._from = sw.from || requested.raw    pkg._optional = sw.optional    pkg._development = sw.dev    pkg._inBundle = false    return addBundled(pkg).then(() => pkg)  }).then((pkg) => {    var isLink = pkg._requested.type === 'directory'    const child = createChild({      package: pkg,      loaded: false,      parent: tree,      fromShrinkwrap: requested,      path: childPath(tree.path, pkg),      realpath: isLink ? requested.fetchSpec : childPath(tree.realpath, pkg),      children: pkg._bundled || [],      location: (tree.location === '/' ? '' : tree.location + '/') + pkg.name,      fromBundle: null,      isLink: isLink,      isInLink: tree.isLink,      swRequires: sw.requires    })    tree.children.push(child)    if (pkg._bundled) {      delete pkg._bundled      inflateBundled(child, child, child.children)    }    return child  })}function childIsEquivalent (sw, requested, child) {  if (!child) return false  if (child.fromShrinkwrap) 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  if (!isRegistry(requested) && sw.from) return child.package._from === sw.from  if (!isRegistry(requested) && child.package._resolved) return sw.version === child.package._resolved  return child.package.version === sw.version}
 |