Hacked By AnonymousFox
// mixin providing the loadVirtual method
const localeCompare = require('@isaacs/string-locale-compare')('en')
const mapWorkspaces = require('@npmcli/map-workspaces')
const { resolve } = require('path')
const nameFromFolder = require('@npmcli/name-from-folder')
const consistentResolve = require('../consistent-resolve.js')
const Shrinkwrap = require('../shrinkwrap.js')
const Node = require('../node.js')
const Link = require('../link.js')
const relpath = require('../relpath.js')
const calcDepFlags = require('../calc-dep-flags.js')
const rpj = require('read-package-json-fast')
const treeCheck = require('../tree-check.js')
const loadFromShrinkwrap = Symbol('loadFromShrinkwrap')
const resolveNodes = Symbol('resolveNodes')
const resolveLinks = Symbol('resolveLinks')
const assignBundles = Symbol('assignBundles')
const loadRoot = Symbol('loadRoot')
const loadNode = Symbol('loadVirtualNode')
const loadLink = Symbol('loadVirtualLink')
const loadWorkspaces = Symbol.for('loadWorkspaces')
const flagsSuspect = Symbol.for('flagsSuspect')
const reCalcDepFlags = Symbol('reCalcDepFlags')
const checkRootEdges = Symbol('checkRootEdges')
const rootOptionProvided = Symbol('rootOptionProvided')
const depsToEdges = (type, deps) =>
Object.entries(deps).map(d => [type, ...d])
module.exports = cls => class VirtualLoader extends cls {
constructor (options) {
super(options)
// the virtual tree we load from a shrinkwrap
this.virtualTree = options.virtualTree
this[flagsSuspect] = false
}
// public method
async loadVirtual (options = {}) {
if (this.virtualTree) {
return this.virtualTree
}
// allow the user to set reify options on the ctor as well.
// XXX: deprecate separate reify() options object.
options = { ...this.options, ...options }
if (options.root && options.root.meta) {
await this[loadFromShrinkwrap](options.root.meta, options.root)
return treeCheck(this.virtualTree)
}
const s = await Shrinkwrap.load({
path: this.path,
lockfileVersion: this.options.lockfileVersion,
resolveOptions: this.options,
})
if (!s.loadedFromDisk && !options.root) {
const er = new Error('loadVirtual requires existing shrinkwrap file')
throw Object.assign(er, { code: 'ENOLOCK' })
}
// when building the ideal tree, we pass in a root node to this function
// otherwise, load it from the root package json or the lockfile
const {
root = await this[loadRoot](s),
} = options
this[rootOptionProvided] = options.root
await this[loadFromShrinkwrap](s, root)
root.assertRootOverrides()
return treeCheck(this.virtualTree)
}
async [loadRoot] (s) {
const pj = this.path + '/package.json'
const pkg = await rpj(pj).catch(() => s.data.packages['']) || {}
return this[loadWorkspaces](this[loadNode]('', pkg, true))
}
async [loadFromShrinkwrap] (s, root) {
if (!this[rootOptionProvided]) {
// root is never any of these things, but might be a brand new
// baby Node object that never had its dep flags calculated.
root.extraneous = false
root.dev = false
root.optional = false
root.devOptional = false
root.peer = false
} else {
this[flagsSuspect] = true
}
this[checkRootEdges](s, root)
root.meta = s
this.virtualTree = root
const { links, nodes } = this[resolveNodes](s, root)
await this[resolveLinks](links, nodes)
if (!(s.originalLockfileVersion >= 2)) {
this[assignBundles](nodes)
}
if (this[flagsSuspect]) {
this[reCalcDepFlags](nodes.values())
}
return root
}
[reCalcDepFlags] (nodes) {
// reset all dep flags
// can't use inventory here, because virtualTree might not be root
for (const node of nodes) {
if (node.isRoot || node === this[rootOptionProvided]) {
continue
}
node.extraneous = true
node.dev = true
node.optional = true
node.devOptional = true
node.peer = true
}
calcDepFlags(this.virtualTree, !this[rootOptionProvided])
}
// check the lockfile deps, and see if they match. if they do not
// then we have to reset dep flags at the end. for example, if the
// user manually edits their package.json file, then we need to know
// that the idealTree is no longer entirely trustworthy.
[checkRootEdges] (s, root) {
// loaded virtually from tree, no chance of being out of sync
// ancient lockfiles are critically damaged by this process,
// so we need to just hope for the best in those cases.
if (!s.loadedFromDisk || s.ancientLockfile) {
return
}
const lock = s.get('')
const prod = lock.dependencies || {}
const dev = lock.devDependencies || {}
const optional = lock.optionalDependencies || {}
const peer = lock.peerDependencies || {}
const peerOptional = {}
if (lock.peerDependenciesMeta) {
for (const [name, meta] of Object.entries(lock.peerDependenciesMeta)) {
if (meta.optional && peer[name] !== undefined) {
peerOptional[name] = peer[name]
delete peer[name]
}
}
}
for (const name of Object.keys(optional)) {
delete prod[name]
}
const lockWS = []
const workspaces = mapWorkspaces.virtual({
cwd: this.path,
lockfile: s.data,
})
for (const [name, path] of workspaces.entries()) {
lockWS.push(['workspace', name, `file:${path.replace(/#/g, '%23')}`])
}
const lockEdges = [
...depsToEdges('prod', prod),
...depsToEdges('dev', dev),
...depsToEdges('optional', optional),
...depsToEdges('peer', peer),
...depsToEdges('peerOptional', peerOptional),
...lockWS,
].sort(([atype, aname], [btype, bname]) =>
localeCompare(atype, btype) || localeCompare(aname, bname))
const rootEdges = [...root.edgesOut.values()]
.map(e => [e.type, e.name, e.spec])
.sort(([atype, aname], [btype, bname]) =>
localeCompare(atype, btype) || localeCompare(aname, bname))
if (rootEdges.length !== lockEdges.length) {
// something added or removed
return this[flagsSuspect] = true
}
for (let i = 0; i < lockEdges.length; i++) {
if (rootEdges[i][0] !== lockEdges[i][0] ||
rootEdges[i][1] !== lockEdges[i][1] ||
rootEdges[i][2] !== lockEdges[i][2]) {
return this[flagsSuspect] = true
}
}
}
// separate out link metadatas, and create Node objects for nodes
[resolveNodes] (s, root) {
const links = new Map()
const nodes = new Map([['', root]])
for (const [location, meta] of Object.entries(s.data.packages)) {
// skip the root because we already got it
if (!location) {
continue
}
if (meta.link) {
links.set(location, meta)
} else {
nodes.set(location, this[loadNode](location, meta))
}
}
return { links, nodes }
}
// links is the set of metadata, and nodes is the map of non-Link nodes
// Set the targets to nodes in the set, if we have them (we might not)
async [resolveLinks] (links, nodes) {
for (const [location, meta] of links.entries()) {
const targetPath = resolve(this.path, meta.resolved)
const targetLoc = relpath(this.path, targetPath)
const target = nodes.get(targetLoc)
const link = this[loadLink](location, targetLoc, target, meta)
nodes.set(location, link)
nodes.set(targetLoc, link.target)
// we always need to read the package.json for link targets
// outside node_modules because they can be changed by the local user
if (!link.target.parent) {
const pj = link.realpath + '/package.json'
const pkg = await rpj(pj).catch(() => null)
if (pkg) {
link.target.package = pkg
}
}
}
}
[assignBundles] (nodes) {
for (const [location, node] of nodes) {
// Skip assignment of parentage for the root package
if (!location || node.isLink && !node.target.location) {
continue
}
const { name, parent, package: { inBundle } } = node
if (!parent) {
continue
}
// read inBundle from package because 'package' here is
// actually a v2 lockfile metadata entry.
// If the *parent* is also bundled, though, or if the parent has
// no dependency on it, then we assume that it's being pulled in
// just by virtue of its parent or a transitive dep being bundled.
const { package: ppkg } = parent
const { inBundle: parentBundled } = ppkg
if (inBundle && !parentBundled && parent.edgesOut.has(node.name)) {
if (!ppkg.bundleDependencies) {
ppkg.bundleDependencies = [name]
} else {
ppkg.bundleDependencies.push(name)
}
}
}
}
[loadNode] (location, sw, loadOverrides) {
const p = this.virtualTree ? this.virtualTree.realpath : this.path
const path = resolve(p, location)
// shrinkwrap doesn't include package name unless necessary
if (!sw.name) {
sw.name = nameFromFolder(path)
}
const dev = sw.dev
const optional = sw.optional
const devOptional = dev || optional || sw.devOptional
const peer = sw.peer
const node = new Node({
installLinks: this.installLinks,
legacyPeerDeps: this.legacyPeerDeps,
root: this.virtualTree,
path,
realpath: path,
integrity: sw.integrity,
resolved: consistentResolve(sw.resolved, this.path, path),
pkg: sw,
hasShrinkwrap: sw.hasShrinkwrap,
dev,
optional,
devOptional,
peer,
loadOverrides,
})
// cast to boolean because they're undefined in the lock file when false
node.extraneous = !!sw.extraneous
node.devOptional = !!(sw.devOptional || sw.dev || sw.optional)
node.peer = !!sw.peer
node.optional = !!sw.optional
node.dev = !!sw.dev
return node
}
[loadLink] (location, targetLoc, target, meta) {
const path = resolve(this.path, location)
const link = new Link({
installLinks: this.installLinks,
legacyPeerDeps: this.legacyPeerDeps,
path,
realpath: resolve(this.path, targetLoc),
target,
pkg: target && target.package,
})
link.extraneous = target.extraneous
link.devOptional = target.devOptional
link.peer = target.peer
link.optional = target.optional
link.dev = target.dev
return link
}
}
Hacked By AnonymousFox1.0, Coded By AnonymousFox