11cb0ef41Sopenharmony_ci'use strict'
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ci// walk the tree of deps starting from the top level list of bundled deps
41cb0ef41Sopenharmony_ci// Any deps at the top level that are depended on by a bundled dep that
51cb0ef41Sopenharmony_ci// does not have that dep in its own node_modules folder are considered
61cb0ef41Sopenharmony_ci// bundled deps as well.  This list of names can be passed to npm-packlist
71cb0ef41Sopenharmony_ci// as the "bundled" argument.  Additionally, packageJsonCache is shared so
81cb0ef41Sopenharmony_ci// packlist doesn't have to re-read files already consumed in this pass
91cb0ef41Sopenharmony_ci
101cb0ef41Sopenharmony_ciconst fs = require('fs')
111cb0ef41Sopenharmony_ciconst path = require('path')
121cb0ef41Sopenharmony_ciconst EE = require('events').EventEmitter
131cb0ef41Sopenharmony_ci// we don't care about the package bins, but we share a pj cache
141cb0ef41Sopenharmony_ci// with other modules that DO care about it, so keep it nice.
151cb0ef41Sopenharmony_ciconst normalizePackageBin = require('npm-normalize-package-bin')
161cb0ef41Sopenharmony_ci
171cb0ef41Sopenharmony_ciclass BundleWalker extends EE {
181cb0ef41Sopenharmony_ci  constructor (opt) {
191cb0ef41Sopenharmony_ci    opt = opt || {}
201cb0ef41Sopenharmony_ci    super(opt)
211cb0ef41Sopenharmony_ci    this.path = path.resolve(opt.path || process.cwd())
221cb0ef41Sopenharmony_ci
231cb0ef41Sopenharmony_ci    this.parent = opt.parent || null
241cb0ef41Sopenharmony_ci    if (this.parent) {
251cb0ef41Sopenharmony_ci      this.result = this.parent.result
261cb0ef41Sopenharmony_ci      // only collect results in node_modules folders at the top level
271cb0ef41Sopenharmony_ci      // since the node_modules in a bundled dep is included always
281cb0ef41Sopenharmony_ci      if (!this.parent.parent) {
291cb0ef41Sopenharmony_ci        const base = path.basename(this.path)
301cb0ef41Sopenharmony_ci        const scope = path.basename(path.dirname(this.path))
311cb0ef41Sopenharmony_ci        this.result.add(/^@/.test(scope) ? scope + '/' + base : base)
321cb0ef41Sopenharmony_ci      }
331cb0ef41Sopenharmony_ci      this.root = this.parent.root
341cb0ef41Sopenharmony_ci      this.packageJsonCache = this.parent.packageJsonCache
351cb0ef41Sopenharmony_ci    } else {
361cb0ef41Sopenharmony_ci      this.result = new Set()
371cb0ef41Sopenharmony_ci      this.root = this.path
381cb0ef41Sopenharmony_ci      this.packageJsonCache = opt.packageJsonCache || new Map()
391cb0ef41Sopenharmony_ci    }
401cb0ef41Sopenharmony_ci
411cb0ef41Sopenharmony_ci    this.seen = new Set()
421cb0ef41Sopenharmony_ci    this.didDone = false
431cb0ef41Sopenharmony_ci    this.children = 0
441cb0ef41Sopenharmony_ci    this.node_modules = []
451cb0ef41Sopenharmony_ci    this.package = null
461cb0ef41Sopenharmony_ci    this.bundle = null
471cb0ef41Sopenharmony_ci  }
481cb0ef41Sopenharmony_ci
491cb0ef41Sopenharmony_ci  addListener (ev, fn) {
501cb0ef41Sopenharmony_ci    return this.on(ev, fn)
511cb0ef41Sopenharmony_ci  }
521cb0ef41Sopenharmony_ci
531cb0ef41Sopenharmony_ci  on (ev, fn) {
541cb0ef41Sopenharmony_ci    const ret = super.on(ev, fn)
551cb0ef41Sopenharmony_ci    if (ev === 'done' && this.didDone) {
561cb0ef41Sopenharmony_ci      this.emit('done', this.result)
571cb0ef41Sopenharmony_ci    }
581cb0ef41Sopenharmony_ci    return ret
591cb0ef41Sopenharmony_ci  }
601cb0ef41Sopenharmony_ci
611cb0ef41Sopenharmony_ci  done () {
621cb0ef41Sopenharmony_ci    if (!this.didDone) {
631cb0ef41Sopenharmony_ci      this.didDone = true
641cb0ef41Sopenharmony_ci      if (!this.parent) {
651cb0ef41Sopenharmony_ci        const res = Array.from(this.result)
661cb0ef41Sopenharmony_ci        this.result = res
671cb0ef41Sopenharmony_ci        this.emit('done', res)
681cb0ef41Sopenharmony_ci      } else {
691cb0ef41Sopenharmony_ci        this.emit('done')
701cb0ef41Sopenharmony_ci      }
711cb0ef41Sopenharmony_ci    }
721cb0ef41Sopenharmony_ci  }
731cb0ef41Sopenharmony_ci
741cb0ef41Sopenharmony_ci  start () {
751cb0ef41Sopenharmony_ci    const pj = path.resolve(this.path, 'package.json')
761cb0ef41Sopenharmony_ci    if (this.packageJsonCache.has(pj)) {
771cb0ef41Sopenharmony_ci      this.onPackage(this.packageJsonCache.get(pj))
781cb0ef41Sopenharmony_ci    } else {
791cb0ef41Sopenharmony_ci      this.readPackageJson(pj)
801cb0ef41Sopenharmony_ci    }
811cb0ef41Sopenharmony_ci    return this
821cb0ef41Sopenharmony_ci  }
831cb0ef41Sopenharmony_ci
841cb0ef41Sopenharmony_ci  readPackageJson (pj) {
851cb0ef41Sopenharmony_ci    fs.readFile(pj, (er, data) =>
861cb0ef41Sopenharmony_ci      er ? this.done() : this.onPackageJson(pj, data))
871cb0ef41Sopenharmony_ci  }
881cb0ef41Sopenharmony_ci
891cb0ef41Sopenharmony_ci  onPackageJson (pj, data) {
901cb0ef41Sopenharmony_ci    try {
911cb0ef41Sopenharmony_ci      this.package = normalizePackageBin(JSON.parse(data + ''))
921cb0ef41Sopenharmony_ci    } catch (er) {
931cb0ef41Sopenharmony_ci      return this.done()
941cb0ef41Sopenharmony_ci    }
951cb0ef41Sopenharmony_ci    this.packageJsonCache.set(pj, this.package)
961cb0ef41Sopenharmony_ci    this.onPackage(this.package)
971cb0ef41Sopenharmony_ci  }
981cb0ef41Sopenharmony_ci
991cb0ef41Sopenharmony_ci  allDepsBundled (pkg) {
1001cb0ef41Sopenharmony_ci    return Object.keys(pkg.dependencies || {}).concat(
1011cb0ef41Sopenharmony_ci      Object.keys(pkg.optionalDependencies || {}))
1021cb0ef41Sopenharmony_ci  }
1031cb0ef41Sopenharmony_ci
1041cb0ef41Sopenharmony_ci  onPackage (pkg) {
1051cb0ef41Sopenharmony_ci    // all deps are bundled if we got here as a child.
1061cb0ef41Sopenharmony_ci    // otherwise, only bundle bundledDeps
1071cb0ef41Sopenharmony_ci    // Get a unique-ified array with a short-lived Set
1081cb0ef41Sopenharmony_ci    const bdRaw = this.parent ? this.allDepsBundled(pkg)
1091cb0ef41Sopenharmony_ci      : pkg.bundleDependencies || pkg.bundledDependencies || []
1101cb0ef41Sopenharmony_ci
1111cb0ef41Sopenharmony_ci    const bd = Array.from(new Set(
1121cb0ef41Sopenharmony_ci      Array.isArray(bdRaw) ? bdRaw
1131cb0ef41Sopenharmony_ci      : bdRaw === true ? this.allDepsBundled(pkg)
1141cb0ef41Sopenharmony_ci      : Object.keys(bdRaw)))
1151cb0ef41Sopenharmony_ci
1161cb0ef41Sopenharmony_ci    if (!bd.length) {
1171cb0ef41Sopenharmony_ci      return this.done()
1181cb0ef41Sopenharmony_ci    }
1191cb0ef41Sopenharmony_ci
1201cb0ef41Sopenharmony_ci    this.bundle = bd
1211cb0ef41Sopenharmony_ci    this.readModules()
1221cb0ef41Sopenharmony_ci  }
1231cb0ef41Sopenharmony_ci
1241cb0ef41Sopenharmony_ci  readModules () {
1251cb0ef41Sopenharmony_ci    readdirNodeModules(this.path + '/node_modules', (er, nm) =>
1261cb0ef41Sopenharmony_ci      er ? this.onReaddir([]) : this.onReaddir(nm))
1271cb0ef41Sopenharmony_ci  }
1281cb0ef41Sopenharmony_ci
1291cb0ef41Sopenharmony_ci  onReaddir (nm) {
1301cb0ef41Sopenharmony_ci    // keep track of what we have, in case children need it
1311cb0ef41Sopenharmony_ci    this.node_modules = nm
1321cb0ef41Sopenharmony_ci
1331cb0ef41Sopenharmony_ci    this.bundle.forEach(dep => this.childDep(dep))
1341cb0ef41Sopenharmony_ci    if (this.children === 0) {
1351cb0ef41Sopenharmony_ci      this.done()
1361cb0ef41Sopenharmony_ci    }
1371cb0ef41Sopenharmony_ci  }
1381cb0ef41Sopenharmony_ci
1391cb0ef41Sopenharmony_ci  childDep (dep) {
1401cb0ef41Sopenharmony_ci    if (this.node_modules.indexOf(dep) !== -1) {
1411cb0ef41Sopenharmony_ci      if (!this.seen.has(dep)) {
1421cb0ef41Sopenharmony_ci        this.seen.add(dep)
1431cb0ef41Sopenharmony_ci        this.child(dep)
1441cb0ef41Sopenharmony_ci      }
1451cb0ef41Sopenharmony_ci    } else if (this.parent) {
1461cb0ef41Sopenharmony_ci      this.parent.childDep(dep)
1471cb0ef41Sopenharmony_ci    }
1481cb0ef41Sopenharmony_ci  }
1491cb0ef41Sopenharmony_ci
1501cb0ef41Sopenharmony_ci  child (dep) {
1511cb0ef41Sopenharmony_ci    const p = this.path + '/node_modules/' + dep
1521cb0ef41Sopenharmony_ci    this.children += 1
1531cb0ef41Sopenharmony_ci    const child = new BundleWalker({
1541cb0ef41Sopenharmony_ci      path: p,
1551cb0ef41Sopenharmony_ci      parent: this,
1561cb0ef41Sopenharmony_ci    })
1571cb0ef41Sopenharmony_ci    child.on('done', _ => {
1581cb0ef41Sopenharmony_ci      if (--this.children === 0) {
1591cb0ef41Sopenharmony_ci        this.done()
1601cb0ef41Sopenharmony_ci      }
1611cb0ef41Sopenharmony_ci    })
1621cb0ef41Sopenharmony_ci    child.start()
1631cb0ef41Sopenharmony_ci  }
1641cb0ef41Sopenharmony_ci}
1651cb0ef41Sopenharmony_ci
1661cb0ef41Sopenharmony_ciclass BundleWalkerSync extends BundleWalker {
1671cb0ef41Sopenharmony_ci  start () {
1681cb0ef41Sopenharmony_ci    super.start()
1691cb0ef41Sopenharmony_ci    this.done()
1701cb0ef41Sopenharmony_ci    return this
1711cb0ef41Sopenharmony_ci  }
1721cb0ef41Sopenharmony_ci
1731cb0ef41Sopenharmony_ci  readPackageJson (pj) {
1741cb0ef41Sopenharmony_ci    try {
1751cb0ef41Sopenharmony_ci      this.onPackageJson(pj, fs.readFileSync(pj))
1761cb0ef41Sopenharmony_ci    } catch {
1771cb0ef41Sopenharmony_ci      // empty catch
1781cb0ef41Sopenharmony_ci    }
1791cb0ef41Sopenharmony_ci    return this
1801cb0ef41Sopenharmony_ci  }
1811cb0ef41Sopenharmony_ci
1821cb0ef41Sopenharmony_ci  readModules () {
1831cb0ef41Sopenharmony_ci    try {
1841cb0ef41Sopenharmony_ci      this.onReaddir(readdirNodeModulesSync(this.path + '/node_modules'))
1851cb0ef41Sopenharmony_ci    } catch {
1861cb0ef41Sopenharmony_ci      this.onReaddir([])
1871cb0ef41Sopenharmony_ci    }
1881cb0ef41Sopenharmony_ci  }
1891cb0ef41Sopenharmony_ci
1901cb0ef41Sopenharmony_ci  child (dep) {
1911cb0ef41Sopenharmony_ci    new BundleWalkerSync({
1921cb0ef41Sopenharmony_ci      path: this.path + '/node_modules/' + dep,
1931cb0ef41Sopenharmony_ci      parent: this,
1941cb0ef41Sopenharmony_ci    }).start()
1951cb0ef41Sopenharmony_ci  }
1961cb0ef41Sopenharmony_ci}
1971cb0ef41Sopenharmony_ci
1981cb0ef41Sopenharmony_ciconst readdirNodeModules = (nm, cb) => {
1991cb0ef41Sopenharmony_ci  fs.readdir(nm, (er, set) => {
2001cb0ef41Sopenharmony_ci    if (er) {
2011cb0ef41Sopenharmony_ci      cb(er)
2021cb0ef41Sopenharmony_ci    } else {
2031cb0ef41Sopenharmony_ci      const scopes = set.filter(f => /^@/.test(f))
2041cb0ef41Sopenharmony_ci      if (!scopes.length) {
2051cb0ef41Sopenharmony_ci        cb(null, set)
2061cb0ef41Sopenharmony_ci      } else {
2071cb0ef41Sopenharmony_ci        const unscoped = set.filter(f => !/^@/.test(f))
2081cb0ef41Sopenharmony_ci        let count = scopes.length
2091cb0ef41Sopenharmony_ci        scopes.forEach(scope => {
2101cb0ef41Sopenharmony_ci          fs.readdir(nm + '/' + scope, (readdirEr, pkgs) => {
2111cb0ef41Sopenharmony_ci            if (readdirEr || !pkgs.length) {
2121cb0ef41Sopenharmony_ci              unscoped.push(scope)
2131cb0ef41Sopenharmony_ci            } else {
2141cb0ef41Sopenharmony_ci              unscoped.push.apply(unscoped, pkgs.map(p => scope + '/' + p))
2151cb0ef41Sopenharmony_ci            }
2161cb0ef41Sopenharmony_ci            if (--count === 0) {
2171cb0ef41Sopenharmony_ci              cb(null, unscoped)
2181cb0ef41Sopenharmony_ci            }
2191cb0ef41Sopenharmony_ci          })
2201cb0ef41Sopenharmony_ci        })
2211cb0ef41Sopenharmony_ci      }
2221cb0ef41Sopenharmony_ci    }
2231cb0ef41Sopenharmony_ci  })
2241cb0ef41Sopenharmony_ci}
2251cb0ef41Sopenharmony_ci
2261cb0ef41Sopenharmony_ciconst readdirNodeModulesSync = nm => {
2271cb0ef41Sopenharmony_ci  const set = fs.readdirSync(nm)
2281cb0ef41Sopenharmony_ci  const unscoped = set.filter(f => !/^@/.test(f))
2291cb0ef41Sopenharmony_ci  const scopes = set.filter(f => /^@/.test(f)).map(scope => {
2301cb0ef41Sopenharmony_ci    try {
2311cb0ef41Sopenharmony_ci      const pkgs = fs.readdirSync(nm + '/' + scope)
2321cb0ef41Sopenharmony_ci      return pkgs.length ? pkgs.map(p => scope + '/' + p) : [scope]
2331cb0ef41Sopenharmony_ci    } catch (er) {
2341cb0ef41Sopenharmony_ci      return [scope]
2351cb0ef41Sopenharmony_ci    }
2361cb0ef41Sopenharmony_ci  }).reduce((a, b) => a.concat(b), [])
2371cb0ef41Sopenharmony_ci  return unscoped.concat(scopes)
2381cb0ef41Sopenharmony_ci}
2391cb0ef41Sopenharmony_ci
2401cb0ef41Sopenharmony_ciconst walk = (options, callback) => {
2411cb0ef41Sopenharmony_ci  const p = new Promise((resolve, reject) => {
2421cb0ef41Sopenharmony_ci    new BundleWalker(options).on('done', resolve).on('error', reject).start()
2431cb0ef41Sopenharmony_ci  })
2441cb0ef41Sopenharmony_ci  return callback ? p.then(res => callback(null, res), callback) : p
2451cb0ef41Sopenharmony_ci}
2461cb0ef41Sopenharmony_ci
2471cb0ef41Sopenharmony_ciconst walkSync = options => {
2481cb0ef41Sopenharmony_ci  return new BundleWalkerSync(options).start().result
2491cb0ef41Sopenharmony_ci}
2501cb0ef41Sopenharmony_ci
2511cb0ef41Sopenharmony_cimodule.exports = walk
2521cb0ef41Sopenharmony_ciwalk.sync = walkSync
2531cb0ef41Sopenharmony_ciwalk.BundleWalker = BundleWalker
2541cb0ef41Sopenharmony_ciwalk.BundleWalkerSync = BundleWalkerSync
255