PNG  IHDRQgAMA a cHRMz&u0`:pQ<bKGDgmIDATxwUﹻ& ^CX(J I@ "% (** BX +*i"]j(IH{~R)[~>h{}gy)I$Ij .I$I$ʊy@}x.: $I$Ii}VZPC)I$IF ^0ʐJ$I$Q^}{"r=OzI$gRZeC.IOvH eKX $IMpxsk.쒷/&r[޳<v| .I~)@$updYRa$I |M.e JaֶpSYR6j>h%IRز if&uJ)M$I vLi=H;7UJ,],X$I1AҒJ$ XY XzI@GNҥRT)E@;]K*Mw;#5_wOn~\ DC&$(A5 RRFkvIR}l!RytRl;~^ǷJj اy뷦BZJr&ӥ8Pjw~vnv X^(I;4R=P[3]J,]ȏ~:3?[ a&e)`e*P[4]T=Cq6R[ ~ޤrXR Հg(t_HZ-Hg M$ãmL5R uk*`%C-E6/%[t X.{8P9Z.vkXŐKjgKZHg(aK9ڦmKjѺm_ \#$5,)-  61eJ,5m| r'= &ڡd%-]J on Xm|{ RҞe $eڧY XYrԮ-a7RK6h>n$5AVڴi*ֆK)mѦtmr1p| q:흺,)Oi*ֺK)ܬ֦K-5r3>0ԔHjJئEZj,%re~/z%jVMڸmrt)3]J,T K֦OvԒgii*bKiNO~%PW0=dii2tJ9Jݕ{7"I P9JKTbu,%r"6RKU}Ij2HKZXJ,妝 XYrP ެ24c%i^IK|.H,%rb:XRl1X4Pe/`x&P8Pj28Mzsx2r\zRPz4J}yP[g=L) .Q[6RjWgp FIH*-`IMRaK9TXcq*I y[jE>cw%gLRԕiFCj-ďa`#e~I j,%r,)?[gp FI˨mnWX#>mʔ XA DZf9,nKҲzIZXJ,L#kiPz4JZF,I,`61%2s $,VOϚ2/UFJfy7K> X+6 STXIeJILzMfKm LRaK9%|4p9LwJI!`NsiazĔ)%- XMq>pk$-$Q2x#N ؎-QR}ᶦHZډ)J,l#i@yn3LN`;nڔ XuX5pF)m|^0(>BHF9(cզEerJI rg7 4I@z0\JIi䵙RR0s;$s6eJ,`n 䂦0a)S)A 1eJ,堌#635RIgpNHuTH_SԕqVe ` &S)>p;S$魁eKIuX`I4춒o}`m$1":PI<[v9^\pTJjriRŭ P{#{R2,`)e-`mgj~1ϣLKam7&U\j/3mJ,`F;M'䱀 .KR#)yhTq;pcK9(q!w?uRR,n.yw*UXj#\]ɱ(qv2=RqfB#iJmmL<]Y͙#$5 uTU7ӦXR+q,`I}qL'`6Kͷ6r,]0S$- [RKR3oiRE|nӦXR.(i:LDLTJjY%o:)6rxzҒqTJjh㞦I.$YR.ʼnGZ\ֿf:%55 I˼!6dKxm4E"mG_ s? .e*?LRfK9%q#uh$)i3ULRfK9yxm܌bj84$i1U^@Wbm4uJ,ҪA>_Ij?1v32[gLRD96oTaR׿N7%L2 NT,`)7&ƝL*꽙yp_$M2#AS,`)7$rkTA29_Iye"|/0t)$n XT2`YJ;6Jx".e<`$) PI$5V4]29SRI>~=@j]lp2`K9Jaai^" Ԋ29ORI%:XV5]JmN9]H;1UC39NI%Xe78t)a;Oi Ҙ>Xt"~G>_mn:%|~ޅ_+]$o)@ǀ{hgN;IK6G&rp)T2i୦KJuv*T=TOSV>(~D>dm,I*Ɛ:R#ۙNI%D>G.n$o;+#RR!.eU˽TRI28t)1LWϚ>IJa3oFbu&:tJ*(F7y0ZR ^p'Ii L24x| XRI%ۄ>S1]Jy[zL$adB7.eh4%%누>WETf+3IR:I3Xה)3אOۦSRO'ٺ)S}"qOr[B7ϙ.edG)^ETR"RtRݜh0}LFVӦDB^k_JDj\=LS(Iv─aTeZ%eUAM-0;~˃@i|l @S4y72>sX-vA}ϛBI!ݎߨWl*)3{'Y|iSlEڻ(5KtSI$Uv02,~ԩ~x;P4ցCrO%tyn425:KMlD ^4JRxSهF_}شJTS6uj+ﷸk$eZO%G*^V2u3EMj3k%)okI]dT)URKDS 7~m@TJR~荪fT"֛L \sM -0T KfJz+nإKr L&j()[E&I ߴ>e FW_kJR|!O:5/2跌3T-'|zX ryp0JS ~^F>-2< `*%ZFP)bSn"L :)+pʷf(pO3TMW$~>@~ū:TAIsV1}S2<%ޟM?@iT ,Eūoz%i~g|`wS(]oȤ8)$ ntu`өe`6yPl IzMI{ʣzʨ )IZ2= ld:5+請M$-ї;U>_gsY$ÁN5WzWfIZ)-yuXIfp~S*IZdt;t>KūKR|$#LcԀ+2\;kJ`]YǔM1B)UbG"IRߊ<xܾӔJ0Z='Y嵤 Leveg)$znV-º^3Ւof#0Tfk^Zs[*I꯳3{)ˬW4Ւ4 OdpbZRS|*I 55#"&-IvT&/윚Ye:i$ 9{LkuRe[I~_\ؠ%>GL$iY8 9ܕ"S`kS.IlC;Ҏ4x&>u_0JLr<J2(^$5L s=MgV ~,Iju> 7r2)^=G$1:3G< `J3~&IR% 6Tx/rIj3O< ʔ&#f_yXJiގNSz; Tx(i8%#4 ~AS+IjerIUrIj362v885+IjAhK__5X%nV%Iͳ-y|7XV2v4fzo_68"S/I-qbf; LkF)KSM$ Ms>K WNV}^`-큧32ŒVؙGdu,^^m%6~Nn&͓3ŒVZMsRpfEW%IwdǀLm[7W&bIRL@Q|)* i ImsIMmKmyV`i$G+R 0tV'!V)֏28vU7͒vHꦼtxꗞT ;S}7Mf+fIRHNZUkUx5SAJㄌ9MqμAIRi|j5)o*^'<$TwI1hEU^c_j?Е$%d`z cyf,XO IJnTgA UXRD }{H}^S,P5V2\Xx`pZ|Yk:$e ~ @nWL.j+ϝYb퇪bZ BVu)u/IJ_ 1[p.p60bC >|X91P:N\!5qUB}5a5ja `ubcVxYt1N0Zzl4]7­gKj]?4ϻ *[bg$)+À*x쳀ogO$~,5 زUS9 lq3+5mgw@np1sso Ӻ=|N6 /g(Wv7U;zωM=wk,0uTg_`_P`uz?2yI!b`kĸSo+Qx%!\οe|އԁKS-s6pu_(ֿ$i++T8=eY; צP+phxWQv*|p1. ά. XRkIQYP,drZ | B%wP|S5`~́@i޾ E;Չaw{o'Q?%iL{u D?N1BD!owPHReFZ* k_-~{E9b-~P`fE{AܶBJAFO wx6Rox5 K5=WwehS8 (JClJ~ p+Fi;ŗo+:bD#g(C"wA^ r.F8L;dzdIHUX݆ϞXg )IFqem%I4dj&ppT{'{HOx( Rk6^C٫O.)3:s(۳(Z?~ٻ89zmT"PLtw䥈5&b<8GZ-Y&K?e8,`I6e(֍xb83 `rzXj)F=l($Ij 2*(F?h(/9ik:I`m#p3MgLaKjc/U#n5S# m(^)=y=đx8ŬI[U]~SцA4p$-F i(R,7Cx;X=cI>{Km\ o(Tv2vx2qiiDJN,Ҏ!1f 5quBj1!8 rDFd(!WQl,gSkL1Bxg''՞^ǘ;pQ P(c_ IRujg(Wz bs#P­rz> k c&nB=q+ؔXn#r5)co*Ũ+G?7< |PQӣ'G`uOd>%Mctz# Ԫڞ&7CaQ~N'-P.W`Oedp03C!IZcIAMPUۀ5J<\u~+{9(FbbyAeBhOSܳ1 bÈT#ŠyDžs,`5}DC-`̞%r&ڙa87QWWp6e7 Rϫ/oY ꇅ Nܶըtc!LA T7V4Jsū I-0Pxz7QNF_iZgúWkG83 0eWr9 X]㾮݁#Jˢ C}0=3ݱtBi]_ &{{[/o[~ \q鯜00٩|cD3=4B_b RYb$óBRsf&lLX#M*C_L܄:gx)WΘsGSbuL rF$9';\4Ɍq'n[%p.Q`u hNb`eCQyQ|l_C>Lb꟟3hSb #xNxSs^ 88|Mz)}:](vbۢamŖ࿥ 0)Q7@0=?^k(*J}3ibkFn HjB׻NO z x}7p 0tfDX.lwgȔhԾŲ }6g E |LkLZteu+=q\Iv0쮑)QٵpH8/2?Σo>Jvppho~f>%bMM}\//":PTc(v9v!gոQ )UfVG+! 35{=x\2+ki,y$~A1iC6#)vC5^>+gǵ@1Hy٪7u;p psϰu/S <aʸGu'tD1ԝI<pg|6j'p:tպhX{o(7v],*}6a_ wXRk,O]Lܳ~Vo45rp"N5k;m{rZbΦ${#)`(Ŵg,;j%6j.pyYT?}-kBDc3qA`NWQū20/^AZW%NQ MI.X#P#,^Ebc&?XR tAV|Y.1!؅⨉ccww>ivl(JT~ u`ٵDm q)+Ri x/x8cyFO!/*!/&,7<.N,YDŽ&ܑQF1Bz)FPʛ?5d 6`kQձ λc؎%582Y&nD_$Je4>a?! ͨ|ȎWZSsv8 j(I&yj Jb5m?HWp=g}G3#|I,5v珿] H~R3@B[☉9Ox~oMy=J;xUVoj bUsl_35t-(ՃɼRB7U!qc+x4H_Qo֮$[GO<4`&č\GOc[.[*Af%mG/ ňM/r W/Nw~B1U3J?P&Y )`ѓZ1p]^l“W#)lWZilUQu`-m|xĐ,_ƪ|9i:_{*(3Gѧ}UoD+>m_?VPۅ15&}2|/pIOʵ> GZ9cmíتmnz)yߐbD >e}:) r|@R5qVSA10C%E_'^8cR7O;6[eKePGϦX7jb}OTGO^jn*媓7nGMC t,k31Rb (vyܴʭ!iTh8~ZYZp(qsRL ?b}cŨʊGO^!rPJO15MJ[c&~Z`"ѓޔH1C&^|Ш|rʼ,AwĴ?b5)tLU)F| &g٣O]oqSUjy(x<Ϳ3 .FSkoYg2 \_#wj{u'rQ>o;%n|F*O_L"e9umDds?.fuuQbIWz |4\0 sb;OvxOSs; G%T4gFRurj(֍ڑb uԖKDu1MK{1^ q; C=6\8FR艇!%\YÔU| 88m)֓NcLve C6z;o&X x59:q61Z(T7>C?gcļxѐ Z oo-08jہ x,`' ҔOcRlf~`jj".Nv+sM_]Zk g( UOPyεx%pUh2(@il0ݽQXxppx-NS( WO+轾 nFߢ3M<;z)FBZjciu/QoF 7R¥ ZFLF~#ȣߨ^<쩡ݛкvџ))ME>ώx4m#!-m!L;vv#~Y[đKmx9.[,UFS CVkZ +ߟrY٧IZd/ioi$%͝ب_ֶX3ܫhNU ZZgk=]=bbJS[wjU()*I =ώ:}-蹞lUj:1}MWm=̛ _ ¾,8{__m{_PVK^n3esw5ӫh#$-q=A̟> ,^I}P^J$qY~Q[ Xq9{#&T.^GVj__RKpn,b=`żY@^՝;z{paVKkQXj/)y TIc&F;FBG7wg ZZDG!x r_tƢ!}i/V=M/#nB8 XxЫ ^@CR<{䤭YCN)eKOSƟa $&g[i3.C6xrOc8TI;o hH6P&L{@q6[ Gzp^71j(l`J}]e6X☉#͕ ׈$AB1Vjh㭦IRsqFBjwQ_7Xk>y"N=MB0 ,C #o6MRc0|$)ف"1!ixY<B9mx `,tA>)5ػQ?jQ?cn>YZe Tisvh# GMމȇp:ԴVuږ8ɼH]C.5C!UV;F`mbBk LTMvPʍϤj?ԯ/Qr1NB`9s"s TYsz &9S%U԰> {<ؿSMxB|H\3@!U| k']$U+> |HHMLޢ?V9iD!-@x TIî%6Z*9X@HMW#?nN ,oe6?tQwڱ.]-y':mW0#!J82qFjH -`ѓ&M0u Uγmxϵ^-_\])@0Rt.8/?ٰCY]x}=sD3ojަЫNuS%U}ԤwHH>ڗjܷ_3gN q7[q2la*ArǓԖ+p8/RGM ]jacd(JhWko6ڎbj]i5Bj3+3!\j1UZLsLTv8HHmup<>gKMJj0@H%,W΃7R) ">c, xixј^ aܖ>H[i.UIHc U1=yW\=S*GR~)AF=`&2h`DzT󑓶J+?W+}C%P:|0H܆}-<;OC[~o.$~i}~HQ TvXΈr=b}$vizL4:ȰT|4~*!oXQR6Lk+#t/g lԁߖ[Jڶ_N$k*". xsxX7jRVbAAʯKҎU3)zSNN _'s?f)6X!%ssAkʱ>qƷb hg %n ~p1REGMHH=BJiy[<5 ǁJҖgKR*倳e~HUy)Ag,K)`Vw6bRR:qL#\rclK/$sh*$ 6덤 KԖc 3Z9=Ɣ=o>X Ώ"1 )a`SJJ6k(<c e{%kϊP+SL'TcMJWRm ŏ"w)qc ef꒵i?b7b('"2r%~HUS1\<(`1Wx9=8HY9m:X18bgD1u ~|H;K-Uep,, C1 RV.MR5άh,tWO8WC$ XRVsQS]3GJ|12 [vM :k#~tH30Rf-HYݺ-`I9%lIDTm\ S{]9gOڒMNCV\G*2JRŨ;Rҏ^ڽ̱mq1Eu?To3I)y^#jJw^Ńj^vvlB_⋌P4x>0$c>K†Aļ9s_VjTt0l#m>E-,,x,-W)سo&96RE XR.6bXw+)GAEvL)͞K4$p=Ũi_ѱOjb HY/+@θH9޼]Nԥ%n{ &zjT? Ty) s^ULlb,PiTf^<À] 62R^V7)S!nllS6~͝V}-=%* ʻ>G DnK<y&>LPy7'r=Hj 9V`[c"*^8HpcO8bnU`4JȪAƋ#1_\ XϘHPRgik(~G~0DAA_2p|J묭a2\NCr]M_0 ^T%e#vD^%xy-n}-E\3aS%yN!r_{ )sAw ڼp1pEAk~v<:`'ӭ^5 ArXOI驻T (dk)_\ PuA*BY]yB"l\ey hH*tbK)3 IKZ򹞋XjN n *n>k]X_d!ryBH ]*R 0(#'7 %es9??ښFC,ՁQPjARJ\Ρw K#jahgw;2$l*) %Xq5!U᢯6Re] |0[__64ch&_}iL8KEgҎ7 M/\`|.p,~`a=BR?xܐrQ8K XR2M8f ?`sgWS%" Ԉ 7R%$ N}?QL1|-эټwIZ%pvL3Hk>,ImgW7{E xPHx73RA @RS CC !\ȟ5IXR^ZxHл$Q[ŝ40 (>+ _C >BRt<,TrT {O/H+˟Pl6 I B)/VC<6a2~(XwV4gnXR ϱ5ǀHٻ?tw똤Eyxp{#WK qG%5],(0ӈH HZ])ג=K1j&G(FbM@)%I` XRg ʔ KZG(vP,<`[ Kn^ SJRsAʠ5xՅF`0&RbV tx:EaUE/{fi2;.IAwW8/tTxAGOoN?G}l L(n`Zv?pB8K_gI+ܗ #i?ޙ.) p$utc ~DžfՈEo3l/)I-U?aԅ^jxArA ΧX}DmZ@QLےbTXGd.^|xKHR{|ΕW_h] IJ`[G9{).y) 0X YA1]qp?p_k+J*Y@HI>^?gt.06Rn ,` ?);p pSF9ZXLBJPWjgQ|&)7! HjQt<| ؅W5 x W HIzYoVMGP Hjn`+\(dNW)F+IrS[|/a`K|ͻ0Hj{R,Q=\ (F}\WR)AgSG`IsnAR=|8$}G(vC$)s FBJ?]_u XRvύ6z ŨG[36-T9HzpW̞ú Xg큽=7CufzI$)ki^qk-) 0H*N` QZkk]/tnnsI^Gu't=7$ Z;{8^jB% IItRQS7[ϭ3 $_OQJ`7!]W"W,)Iy W AJA;KWG`IY{8k$I$^%9.^(`N|LJ%@$I}ֽp=FB*xN=gI?Q{٥4B)mw $Igc~dZ@G9K X?7)aK%݅K$IZ-`IpC U6$I\0>!9k} Xa IIS0H$I H ?1R.Чj:4~Rw@p$IrA*u}WjWFPJ$I➓/6#! LӾ+ X36x8J |+L;v$Io4301R20M I$-E}@,pS^ޟR[/s¹'0H$IKyfŸfVOπFT*a$I>He~VY/3R/)>d$I>28`Cjw,n@FU*9ttf$I~<;=/4RD~@ X-ѕzἱI$: ԍR a@b X{+Qxuq$IЛzo /~3\8ڒ4BN7$IҀj V]n18H$IYFBj3̵̚ja pp $Is/3R Ӻ-Yj+L;.0ŔI$Av? #!5"aʄj}UKmɽH$IjCYs?h$IDl843.v}m7UiI=&=0Lg0$I4: embe` eQbm0u? $IT!Sƍ'-sv)s#C0:XB2a w I$zbww{."pPzO =Ɔ\[ o($Iaw]`E).Kvi:L*#gР7[$IyGPI=@R 4yR~̮´cg I$I/<tPͽ hDgo 94Z^k盇΄8I56^W$I^0̜N?4*H`237}g+hxoq)SJ@p|` $I%>-hO0eO>\ԣNߌZD6R=K ~n($I$y3D>o4b#px2$yڪtzW~a $I~?x'BwwpH$IZݑnC㧄Pc_9sO gwJ=l1:mKB>Ab<4Lp$Ib o1ZQ@85b̍ S'F,Fe,^I$IjEdù{l4 8Ys_s Z8.x m"+{~?q,Z D!I$ϻ'|XhB)=…']M>5 rgotԎ 獽PH$IjIPhh)n#cÔqA'ug5qwU&rF|1E%I$%]!'3AFD/;Ck_`9 v!ٴtPV;x`'*bQa w I$Ix5 FC3D_~A_#O݆DvV?<qw+I$I{=Z8".#RIYyjǪ=fDl9%M,a8$I$Ywi[7ݍFe$s1ՋBVA?`]#!oz4zjLJo8$I$%@3jAa4(o ;p,,dya=F9ً[LSPH$IJYЉ+3> 5"39aZ<ñh!{TpBGkj}Sp $IlvF.F$I z< '\K*qq.f<2Y!S"-\I$IYwčjF$ w9 \ߪB.1v!Ʊ?+r:^!I$BϹB H"B;L'G[ 4U#5>੐)|#o0aڱ$I>}k&1`U#V?YsV x>{t1[I~D&(I$I/{H0fw"q"y%4 IXyE~M3 8XψL}qE$I[> nD?~sf ]o΁ cT6"?'_Ἣ $I>~.f|'!N?⟩0G KkXZE]ޡ;/&?k OۘH$IRۀwXӨ<7@PnS04aӶp.:@\IWQJ6sS%I$e5ڑv`3:x';wq_vpgHyXZ 3gЂ7{{EuԹn±}$I$8t;b|591nءQ"P6O5i }iR̈́%Q̄p!I䮢]O{H$IRϻ9s֧ a=`- aB\X0"+5"C1Hb?߮3x3&gşggl_hZ^,`5?ߎvĸ%̀M!OZC2#0x LJ0 Gw$I$I}<{Eb+y;iI,`ܚF:5ܛA8-O-|8K7s|#Z8a&><a&/VtbtLʌI$I$I$I$I$I$IRjDD%tEXtdate:create2022-05-31T04:40:26+00:00!Î%tEXtdate:modify2022-05-31T04:40:26+00:00|{2IENDB`Mini Shell

HOME


Mini Shell 1.0
DIR:/usr/share/nodejs/@npmcli/arborist/lib/
Upload File :
Current File : //usr/share/nodejs/@npmcli/arborist/lib/node.js
// inventory, path, realpath, root, and parent
//
// node.root is a reference to the root module in the tree (ie, typically the
// cwd project folder)
//
// node.location is the /-delimited path from the root module to the node.  In
// the case of link targets that may be outside of the root's package tree,
// this can include some number of /../ path segments.  The location of the
// root module is always '.'.  node.location thus never contains drive letters
// or absolute paths, and is portable within a given project, suitable for
// inclusion in lockfiles and metadata.
//
// node.path is the path to the place where this node lives on disk.  It is
// system-specific and absolute.
//
// node.realpath is the path to where the module actually resides on disk.  In
// the case of non-link nodes, node.realpath is equivalent to node.path.  In
// the case of link nodes, it is equivalent to node.target.path.
//
// Setting node.parent will set the node's root to the parent's root, as well
// as updating edgesIn and edgesOut to reload dependency resolutions as needed,
// and setting node.path to parent.path/node_modules/name.
//
// node.inventory is a Map of name to a Set() of all the nodes under a given
// root by that name.  It's empty for non-root nodes, and changing the root
// reference will remove it from the old root's inventory and add it to the new
// one.  This map is useful for cases like `npm update foo` or `npm ls foo`
// where we need to quickly find all instances of a given package name within a
// tree.

const nameFromFolder = require('@npmcli/name-from-folder')
const Edge = require('./edge.js')
const Inventory = require('./inventory.js')
const {normalize} = require('read-package-json-fast')
const {getPaths: getBinPaths} = require('bin-links')
const npa = require('npm-package-arg')
const debug = require('./debug.js')
const gatherDepSet = require('./gather-dep-set.js')
const treeCheck = require('./tree-check.js')
const walkUp = require('walk-up-path')

const {resolve, relative, dirname, basename} = require('path')
const util = require('util')
const _package = Symbol('_package')
const _parent = Symbol('_parent')
const _target = Symbol.for('_target')
const _fsParent = Symbol('_fsParent')
const _loadDepType = Symbol('_loadDepType')
const _loadWorkspaces = Symbol('_loadWorkspaces')
const _reloadNamedEdges = Symbol('_reloadNamedEdges')
// overridden by Link class
const _loadDeps = Symbol.for('Arborist.Node._loadDeps')
const _root = Symbol('_root')
const _refreshLocation = Symbol.for('_refreshLocation')
const _changePath = Symbol.for('_changePath')
// used by Link class as well
const _delistFromMeta = Symbol.for('_delistFromMeta')
const _global = Symbol.for('global')
const _workspaces = Symbol('_workspaces')
const _explain = Symbol('_explain')
const _explanation = Symbol('_explanation')
const _meta = Symbol('_meta')

const relpath = require('./relpath.js')
const consistentResolve = require('./consistent-resolve.js')

const printableTree = require('./printable.js')

class Node {
  constructor (options) {
    // NB: path can be null if it's a link target
    const {
      root,
      path,
      realpath,
      parent,
      error,
      meta,
      fsParent,
      resolved,
      integrity,
      // allow setting name explicitly when we haven't set a path yet
      name,
      children,
      fsChildren,
      legacyPeerDeps = false,
      linksIn,
      hasShrinkwrap,
      extraneous = true,
      dev = true,
      optional = true,
      devOptional = true,
      peer = true,
      global = false,
      dummy = false,
      sourceReference = null,
    } = options

    // true if part of a global install
    this[_global] = global

    this[_workspaces] = null

    this.errors = error ? [error] : []

    // this will usually be null, except when modeling a
    // package's dependencies in a virtual root.
    this.sourceReference = sourceReference

    const pkg = sourceReference ? sourceReference.package
      : normalize(options.pkg || {})

    this.name = name ||
      nameFromFolder(path || pkg.name || realpath) ||
      pkg.name ||
      null

    // should be equal if not a link
    this.path = path ? resolve(path) : null

    if (!this.name && (!this.path || this.path !== dirname(this.path)))
      throw new TypeError('could not detect node name from path or package')

    this.realpath = !this.isLink ? this.path : resolve(realpath)

    this.resolved = resolved || null
    if (!this.resolved) {
      // note: this *only* works for non-file: deps, so we avoid even
      // trying here.
      // file: deps are tracked in package.json will _resolved set to the
      // full path to the tarball or link target.  However, if the package
      // is checked into git or moved to another location, that's 100% not
      // portable at all!  The _where and _location don't provide much help,
      // since _location is just where the module ended up in the tree,
      // and _where can be different than the actual root if it's a
      // meta-dep deeper in the dependency graph.
      //
      // If we don't have the other oldest indicators of legacy npm, then it's
      // probably what we're getting from pacote, which IS trustworthy.
      //
      // Otherwise, hopefully a shrinkwrap will help us out.
      const resolved = consistentResolve(pkg._resolved)
      if (resolved && !(/^file:/.test(resolved) && pkg._where))
        this.resolved = resolved
    }
    this.integrity = integrity || pkg._integrity || null
    this.hasShrinkwrap = hasShrinkwrap || pkg._hasShrinkwrap || false
    this.legacyPeerDeps = legacyPeerDeps

    this.children = new Map()
    this.fsChildren = new Set()
    this.inventory = new Inventory({})
    this.tops = new Set()
    this.linksIn = new Set(linksIn || [])

    // these three are set by an Arborist taking a catalog
    // after the tree is built.  We don't get this along the way,
    // because they have a tendency to change as new children are
    // added, especially when they're deduped.  Eg, a dev dep may be
    // a 3-levels-deep dependency of a non-dev dep.  If we calc the
    // flags along the way, then they'll tend to be invalid  by the
    // time we need to look at them.
    if (!dummy) {
      this.dev = dev
      this.optional = optional
      this.devOptional = devOptional
      this.peer = peer
      this.extraneous = extraneous
      this.dummy = false
    } else {
      // true if this is a placeholder for the purpose of serving as a
      // fsParent to link targets that get their deps resolved outside
      // the root tree folder.
      this.dummy = true
      this.dev = false
      this.optional = false
      this.devOptional = false
      this.peer = false
      this.extraneous = false
    }

    this.edgesIn = new Set()
    this.edgesOut = new Map()

    // have to set the internal package ref before assigning the parent,
    // because this.package is read when adding to inventory
    this[_package] = pkg && typeof pkg === 'object' ? pkg : {}

    // only relevant for the root and top nodes
    this.meta = meta

    // Note: this is _slightly_ less efficient for the initial tree
    // building than it could be, but in exchange, it's a much simpler
    // algorithm.
    // If this node has a bunch of children, and those children satisfy
    // its various deps, then we're going to _first_ create all the
    // edges, and _then_ assign the children into place, re-resolving
    // them all in _reloadNamedEdges.
    // A more efficient, but more complicated, approach would be to
    // flag this node as being a part of a tree build, so it could
    // hold off on resolving its deps until its children are in place.

    // call the parent setter
    // Must be set prior to calling _loadDeps, because top-ness is relevant

    // will also assign root if present on the parent
    this[_parent] = null
    this.parent = parent || null

    this[_fsParent] = null
    this.fsParent = fsParent || null

    // see parent/root setters below.
    // root is set to parent's root if we have a parent, otherwise if it's
    // null, then it's set to the node itself.
    if (!parent && !fsParent)
      this.root = root || null

    // mostly a convenience for testing, but also a way to create
    // trees in a more declarative way than setting parent on each
    if (children) {
      for (const c of children)
        new Node({ ...c, parent: this })
    }
    if (fsChildren) {
      for (const c of fsChildren)
        new Node({ ...c, fsParent: this })
    }

    // now load all the dep edges
    this[_loadDeps]()
  }

  get meta () {
    return this[_meta]
  }

  set meta (meta) {
    this[_meta] = meta
    if (meta)
      meta.add(this)
  }

  get global () {
    return this.root[_global]
  }

  // true for packages installed directly in the global node_modules folder
  get globalTop () {
    return this.global && this.parent.isProjectRoot
  }

  get workspaces () {
    return this[_workspaces]
  }

  set workspaces (workspaces) {
    // deletes edges if they already exists
    if (this[_workspaces]) {
      for (const name of this[_workspaces].keys()) {
        if (!workspaces.has(name))
          this.edgesOut.get(name).detach()
      }
    }

    this[_workspaces] = workspaces
    this[_loadWorkspaces]()
    this[_loadDeps]()
  }

  get binPaths () {
    if (!this.parent)
      return []

    return getBinPaths({
      pkg: this[_package],
      path: this.path,
      global: this.global,
      top: this.globalTop,
    })
  }

  get hasInstallScript () {
    const {hasInstallScript, scripts} = this.package
    const {install, preinstall, postinstall} = scripts || {}
    return !!(hasInstallScript || install || preinstall || postinstall)
  }

  get version () {
    return this[_package].version || ''
  }

  get pkgid () {
    const { name = '', version = '' } = this.package
    // root package will prefer package name over folder name,
    // and never be called an alias.
    const { isProjectRoot } = this
    const myname = isProjectRoot ? name || this.name
      : this.name
    const alias = !isProjectRoot && name && myname !== name ? `npm:${name}@`
      : ''
    return `${myname}@${alias}${version}`
  }

  get package () {
    return this[_package]
  }

  set package (pkg) {
    // just detach them all.  we could make this _slightly_ more efficient
    // by only detaching the ones that changed, but we'd still have to walk
    // them all, and the comparison logic gets a bit tricky.  we generally
    // only do this more than once at the root level, so the resolve() calls
    // are only one level deep, and there's not much to be saved, anyway.
    // simpler to just toss them all out.
    for (const edge of this.edgesOut.values())
      edge.detach()

    this[_explanation] = null
    /* istanbul ignore next - should be impossible */
    if (!pkg || typeof pkg !== 'object') {
      debug(() => {
        throw new Error('setting Node.package to non-object')
      })
      pkg = {}
    }
    this[_package] = pkg
    this[_loadWorkspaces]()
    this[_loadDeps]()
    // do a hard reload, since the dependents may now be valid or invalid
    // as a result of the package change.
    this.edgesIn.forEach(edge => edge.reload(true))
  }

  // node.explain(nodes seen already, edge we're trying to satisfy
  // if edge is not specified, it lists every edge into the node.
  explain (edge = null, seen = []) {
    if (this[_explanation])
      return this[_explanation]

    return this[_explanation] = this[_explain](edge, seen)
  }

  [_explain] (edge, seen) {
    if (this.isProjectRoot && !this.sourceReference) {
      return {
        location: this.path,
      }
    }

    const why = {
      name: this.isProjectRoot ? this.package.name : this.name,
      version: this.package.version,
    }
    if (this.errors.length || !this.package.name || !this.package.version) {
      why.errors = this.errors.length ? this.errors : [
        new Error('invalid package: lacks name and/or version'),
      ]
      why.package = this.package
    }

    if (this.root.sourceReference) {
      const {name, version} = this.root.package
      why.whileInstalling = {
        name,
        version,
        path: this.root.sourceReference.path,
      }
    }

    if (this.sourceReference)
      return this.sourceReference.explain(edge, seen)

    if (seen.includes(this))
      return why

    why.location = this.location

    // make a new list each time.  we can revisit, but not loop.
    seen = seen.concat(this)

    why.dependents = []
    if (edge)
      why.dependents.push(edge.explain(seen))
    else {
      // ignore invalid edges, since those aren't satisfied by this thing,
      // and are not keeping it held in this spot anyway.
      const edges = []
      for (const edge of this.edgesIn) {
        if (!edge.valid && !edge.from.isProjectRoot)
          continue

        edges.push(edge)
      }
      for (const edge of edges)
        why.dependents.push(edge.explain(seen))
    }
    return why
  }

  isDescendantOf (node) {
    for (let p = this; p; p = p.parent) {
      if (p === node)
        return true
    }
    return false
  }

  getBundler (path = []) {
    // made a cycle, definitely not bundled!
    if (path.includes(this))
      return null

    path.push(this)

    const parent = this[_parent]
    if (!parent)
      return null

    const pBundler = parent.getBundler(path)
    if (pBundler)
      return pBundler

    const ppkg = parent.package
    const bd = ppkg && ppkg.bundleDependencies
    // explicit bundling
    if (Array.isArray(bd) && bd.includes(this.name))
      return parent

    // deps that are deduped up to the bundling level are bundled.
    // however, if they get their dep met further up than that,
    // then they are not bundled.  Ie, installing a package with
    // unmet bundled deps will not cause your deps to be bundled.
    for (const edge of this.edgesIn) {
      const eBundler = edge.from.getBundler(path)
      if (!eBundler)
        continue

      if (eBundler === parent)
        return eBundler
    }

    return null
  }

  get inBundle () {
    return !!this.getBundler()
  }

  // when reifying, if a package is technically in a bundleDependencies list,
  // but that list is the root project, we still have to install it.  This
  // getter returns true if it's in a dependency's bundle list, not the root's.
  get inDepBundle () {
    const bundler = this.getBundler()
    return !!bundler && bundler !== this.root
  }

  get isWorkspace () {
    if (this.isProjectRoot)
      return false
    const { root } = this
    const { type, to } = root.edgesOut.get(this.package.name) || {}
    return type === 'workspace' && to && (to.target === this || to === this)
  }

  get isRoot () {
    return this === this.root
  }

  get isProjectRoot () {
    return this === this.root || this === this.root.target
  }

  set root (root) {
    // setting to null means this is the new root
    // should only ever be one step
    while (root && root.root !== root)
      root = root.root

    root = root || this

    // delete from current root inventory
    this[_delistFromMeta]()

    // can't set the root (yet) if there's no way to determine location
    // this allows us to do new Node({...}) and then set the root later.
    // just make the assignment so we don't lose it, and move on.
    if (!this.path || !root.realpath || !root.path)
      return this[_root] = root

    // temporarily become a root node
    this[_root] = this

    // break all linksIn, we're going to re-set them if needed later
    for (const link of this.linksIn) {
      link[_target] = null
      this.linksIn.delete(link)
    }

    // temporarily break this link as well, we'll re-set if possible later
    const { target } = this
    if (this.isLink) {
      if (target) {
        target.linksIn.delete(this)
        if (target.root === this)
          target[_delistFromMeta]()
      }
      this[_target] = null
    }

    // if this is part of a cascading root set, then don't do this bit
    // but if the parent/fsParent is in a different set, we have to break
    // that reference before proceeding
    if (this.parent && this.parent.root !== root) {
      this.parent.children.delete(this.name)
      this[_parent] = null
    }
    if (this.fsParent && this.fsParent.root !== root) {
      this.fsParent.fsChildren.delete(this)
      this[_fsParent] = null
    }

    if (root === this)
      this[_refreshLocation]()
    else {
      // setting to some different node.
      const loc = relpath(root.realpath, this.path)
      const current = root.inventory.get(loc)

      // clobber whatever is there now
      if (current)
        current.root = null

      this[_root] = root
      // set this.location and add to inventory
      this[_refreshLocation]()

      // try to find our parent/fsParent in the new root inventory
      for (const p of walkUp(dirname(this.path))) {
        const ploc = relpath(root.realpath, p)
        const parent = root.inventory.get(ploc)
        if (parent) {
          /* istanbul ignore next - impossible */
          if (parent.isLink) {
            debug(() => {
              throw Object.assign(new Error('assigning parentage to link'), {
                path: this.path,
                parent: parent.path,
                parentReal: parent.realpath,
              })
            })
            continue
          }
          const childLoc = `${ploc}${ploc ? '/' : ''}node_modules/${this.name}`
          const isParent = this.location === childLoc
          if (isParent) {
            const oldChild = parent.children.get(this.name)
            if (oldChild && oldChild !== this)
              oldChild.root = null
            if (this.parent) {
              this.parent.children.delete(this.name)
              this.parent[_reloadNamedEdges](this.name)
            }
            parent.children.set(this.name, this)
            this[_parent] = parent
            // don't do it for links, because they don't have a target yet
            // we'll hit them up a bit later on.
            if (!this.isLink)
              parent[_reloadNamedEdges](this.name)
          } else {
            /* istanbul ignore if - should be impossible, since we break
             * all fsParent/child relationships when moving? */
            if (this.fsParent)
              this.fsParent.fsChildren.delete(this)
            parent.fsChildren.add(this)
            this[_fsParent] = parent
          }
          break
        }
      }

      // if it doesn't have a parent, it's a top node
      if (!this.parent)
        root.tops.add(this)
      else
        root.tops.delete(this)

      // assign parentage for any nodes that need to have this as a parent
      // this can happen when we have a node at nm/a/nm/b added *before*
      // the node at nm/a, which might have the root node as a fsParent.
      // we can't rely on the public setter here, because it calls into
      // this function to set up these references!
      const nmloc = `${this.location}${this.location ? '/' : ''}node_modules/`
      const isChild = n => n.location === nmloc + n.name
      // check dirname so that /foo isn't treated as the fsparent of /foo-bar
      const isFsChild = n => dirname(n.path).startsWith(this.path) &&
        n !== this &&
        !n.parent &&
        (!n.fsParent || n.fsParent === this || dirname(this.path).startsWith(n.fsParent.path))
      const isKid = n => isChild(n) || isFsChild(n)

      // only walk top nodes, since anything else already has a parent.
      for (const child of root.tops) {
        if (!isKid(child))
          continue

        // set up the internal parentage links
        if (this.isLink)
          child.root = null
        else {
          // can't possibly have a parent, because it's in tops
          if (child.fsParent)
            child.fsParent.fsChildren.delete(child)
          child[_fsParent] = null
          if (isChild(child)) {
            this.children.set(child.name, child)
            child[_parent] = this
            root.tops.delete(child)
          } else {
            this.fsChildren.add(child)
            child[_fsParent] = this
          }
        }
      }

      // look for any nodes with the same realpath.  either they're links
      // to that realpath, or a thing at that realpath if we're adding a link
      // (if we're adding a regular node, we already deleted the old one)
      for (const node of root.inventory.query('realpath', this.realpath)) {
        if (node === this)
          continue

        /* istanbul ignore next - should be impossible */
        debug(() => {
          if (node.root !== root)
            throw new Error('inventory contains node from other root')
        })

        if (this.isLink) {
          const target = node.target || node
          this[_target] = target
          this[_package] = target.package
          target.linksIn.add(this)
          // reload edges here, because now we have a target
          if (this.parent)
            this.parent[_reloadNamedEdges](this.name)
          break
        } else {
          /* istanbul ignore else - should be impossible */
          if (node.isLink) {
            node[_target] = this
            node[_package] = this.package
            this.linksIn.add(node)
            if (node.parent)
              node.parent[_reloadNamedEdges](node.name)
          } else {
            debug(() => {
              throw Object.assign(new Error('duplicate node in root setter'), {
                path: this.path,
                realpath: this.realpath,
                root: root.realpath,
              })
            })
          }
        }
      }
    }

    // reload all edgesIn where the root doesn't match, so we don't have
    // cross-tree dependency graphs
    for (const edge of this.edgesIn) {
      if (edge.from.root !== root)
        edge.reload()
    }
    // reload all edgesOut where root doens't match, or is missing, since
    // it might not be missing in the new tree
    for (const edge of this.edgesOut.values()) {
      if (!edge.to || edge.to.root !== root)
        edge.reload()
    }

    // now make sure our family comes along for the ride!
    const family = new Set([
      ...this.fsChildren,
      ...this.children.values(),
      ...this.inventory.values(),
    ].filter(n => n !== this))
    for (const child of family) {
      if (child.root !== root) {
        child[_delistFromMeta]()
        child[_parent] = null
        this.children.delete(child.name)
        child[_fsParent] = null
        this.fsChildren.delete(child)
        for (const l of child.linksIn) {
          l[_target] = null
          child.linksIn.delete(l)
        }
      }
    }
    for (const child of family) {
      if (child.root !== root)
        child.root = root
    }

    // if we had a target, and didn't find one in the new root, then bring
    // it over as well.
    if (this.isLink && target && !this.target)
      target.root = root

    // tree should always be valid upon root setter completion.
    treeCheck(this)
  }

  get root () {
    return this[_root] || this
  }

  [_loadWorkspaces] () {
    if (!this[_workspaces])
      return

    for (const [name, path] of this[_workspaces].entries())
      new Edge({ from: this, name, spec: `file:${path}`, type: 'workspace' })
  }

  [_loadDeps] () {
    // Caveat!  Order is relevant!
    // packages in optionalDependencies and prod/peer/dev are
    // optional.  Packages in both deps and devDeps are required.
    // Note the subtle breaking change from v6: it is no longer possible
    // to have a different spec for a devDep than production dep.
    this[_loadDepType](this.package.optionalDependencies, 'optional')
    this[_loadDepType](this.package.dependencies, 'prod')

    // Linked targets that are disconnected from the tree are tops,
    // but don't have a 'path' field, only a 'realpath', because we
    // don't know their canonical location. We don't need their devDeps.
    const { isTop, path, sourceReference } = this
    const { isTop: srcTop, path: srcPath } = sourceReference || {}
    if (isTop && path && (!sourceReference || srcTop && srcPath))
      this[_loadDepType](this.package.devDependencies, 'dev')

    const pd = this.package.peerDependencies
    if (pd && typeof pd === 'object' && !this.legacyPeerDeps) {
      const pm = this.package.peerDependenciesMeta || {}
      const peerDependencies = {}
      const peerOptional = {}
      for (const [name, dep] of Object.entries(pd)) {
        if (pm[name] && pm[name].optional)
          peerOptional[name] = dep
        else
          peerDependencies[name] = dep
      }
      this[_loadDepType](peerDependencies, 'peer')
      this[_loadDepType](peerOptional, 'peerOptional')
    }
  }

  [_loadDepType] (obj, type) {
    const from = this
    const ad = this.package.acceptDependencies || {}
    for (const [name, spec] of Object.entries(obj || {})) {
      const accept = ad[name]
      // if it's already set, then we keep the existing edge
      // NB: the Edge ctor adds itself to from.edgesOut
      if (!this.edgesOut.get(name))
        new Edge({ from, name, spec, accept, type })
    }
  }

  get fsParent () {
    return this[_fsParent]
  }

  set fsParent (fsParent) {
    if (!fsParent) {
      if (this[_fsParent])
        this.root = null
      return
    }

    debug(() => {
      if (fsParent === this)
        throw new Error('setting node to its own fsParent')

      if (fsParent.realpath === this.realpath)
        throw new Error('setting fsParent to same path')

      // the initial set MUST be an actual walk-up from the realpath
      // subsequent sets will re-root on the new fsParent's path.
      if (!this[_fsParent] && this.realpath.indexOf(fsParent.realpath) !== 0) {
        throw Object.assign(new Error('setting fsParent improperly'), {
          path: this.path,
          realpath: this.realpath,
          fsParent: {
            path: fsParent.path,
            realpath: fsParent.realpath,
          },
        })
      }
    })

    if (fsParent.isLink)
      fsParent = fsParent.target

    // setting a thing to its own fsParent is not normal, but no-op for safety
    if (this === fsParent || fsParent.realpath === this.realpath)
      return

    // nothing to do
    if (this[_fsParent] === fsParent)
      return

    const oldFsParent = this[_fsParent]
    const newPath = !oldFsParent ? this.path
      : resolve(fsParent.path, relative(oldFsParent.path, this.path))
    const nmPath = resolve(fsParent.path, 'node_modules', this.name)

    // this is actually the parent, set that instead
    if (newPath === nmPath) {
      this.parent = fsParent
      return
    }

    const pathChange = newPath !== this.path

    // remove from old parent/fsParent
    const oldParent = this.parent
    const oldName = this.name
    if (this.parent) {
      this.parent.children.delete(this.name)
      this[_parent] = null
    }
    if (this.fsParent) {
      this.fsParent.fsChildren.delete(this)
      this[_fsParent] = null
    }

    // update this.path/realpath for this and all children/fsChildren
    if (pathChange)
      this[_changePath](newPath)

    if (oldParent)
      oldParent[_reloadNamedEdges](oldName)

    // clobbers anything at that path, resets all appropriate references
    this.root = fsParent.root
  }

  // is it safe to replace one node with another?  check the edges to
  // make sure no one will get upset.  Note that the node might end up
  // having its own unmet dependencies, if the new node has new deps.
  // Note that there are cases where Arborist will opt to insert a node
  // into the tree even though this function returns false!  This is
  // necessary when a root dependency is added or updated, or when a
  // root dependency brings peer deps along with it.  In that case, we
  // will go ahead and create the invalid state, and then try to resolve
  // it with more tree construction, because it's a user request.
  canReplaceWith (node) {
    if (node.name !== this.name)
      return false

    // gather up all the deps of this node and that are only depended
    // upon by deps of this node.  those ones don't count, since
    // they'll be replaced if this node is replaced anyway.
    const depSet = gatherDepSet([this], e => e.to !== this && e.valid)

    for (const edge of this.edgesIn) {
      // only care about edges that don't originate from this node
      if (!depSet.has(edge.from) && !edge.satisfiedBy(node))
        return false
    }

    return true
  }

  canReplace (node) {
    return node.canReplaceWith(this)
  }

  satisfies (requested) {
    if (requested instanceof Edge)
      return this.name === requested.name && requested.satisfiedBy(this)

    const parsed = npa(requested)
    const { name = this.name, rawSpec: spec } = parsed
    return this.name === name && this.satisfies(new Edge({
      from: new Node({ path: this.root.realpath }),
      type: 'prod',
      name,
      spec,
    }))
  }

  matches (node) {
    // if the nodes are literally the same object, obviously a match.
    if (node === this)
      return true

    // if the names don't match, they're different things, even if
    // the package contents are identical.
    if (node.name !== this.name)
      return false

    // if they're links, they match if the targets match
    if (this.isLink)
      return node.isLink && this.target.matches(node.target)

    // if they're two project root nodes, they're different if the paths differ
    if (this.isProjectRoot && node.isProjectRoot)
      return this.path === node.path

    // if the integrity matches, then they're the same.
    if (this.integrity && node.integrity)
      return this.integrity === node.integrity

    // if no integrity, check resolved
    if (this.resolved && node.resolved)
      return this.resolved === node.resolved

    // if no resolved, check both package name and version
    // otherwise, conclude that they are different things
    return this.package.name && node.package.name &&
      this.package.name === node.package.name &&
      this.version && node.version &&
      this.version === node.version
  }

  // replace this node with the supplied argument
  // Useful when mutating an ideal tree, so we can avoid having to call
  // the parent/root setters more than necessary.
  replaceWith (node) {
    node.replace(this)
  }

  replace (node) {
    this[_delistFromMeta]()
    this.path = node.path
    this.name = node.name
    if (!this.isLink)
      this.realpath = this.path
    this[_refreshLocation]()

    // keep children when a node replaces another
    if (!this.isLink) {
      for (const kid of node.children.values())
        kid.parent = this
    }

    if (!node.isRoot)
      this.root = node.root

    treeCheck(this)
  }

  get inShrinkwrap () {
    return this.parent && (this.parent.hasShrinkwrap || this.parent.inShrinkwrap)
  }

  get parent () {
    return this[_parent]
  }

  // This setter keeps everything in order when we move a node from
  // one point in a logical tree to another.  Edges get reloaded,
  // metadata updated, etc.  It's also called when we *replace* a node
  // with another by the same name (eg, to update or dedupe).
  // This does a couple of walks out on the node_modules tree, recursing
  // into child nodes.  However, as setting the parent is typically done
  // with nodes that don't have have many children, and (deduped) package
  // trees tend to be broad rather than deep, it's not that bad.
  // The only walk that starts from the parent rather than this node is
  // limited by edge name.
  set parent (parent) {
    // when setting to null, just remove it from the tree entirely
    if (!parent) {
      // but only delete it if we actually had a parent in the first place
      // otherwise it's just setting to null when it's already null
      if (this[_parent])
        this.root = null
      return
    }

    if (parent.isLink)
      parent = parent.target

    // setting a thing to its own parent is not normal, but no-op for safety
    if (this === parent)
      return

    const oldParent = this[_parent]

    // nothing to do
    if (oldParent === parent)
      return

    // ok now we know something is actually changing, and parent is not a link
    const newPath = resolve(parent.path, 'node_modules', this.name)
    const pathChange = newPath !== this.path

    // remove from old parent/fsParent
    if (oldParent) {
      oldParent.children.delete(this.name)
      this[_parent] = null
    }
    if (this.fsParent) {
      this.fsParent.fsChildren.delete(this)
      this[_fsParent] = null
    }

    // update this.path/realpath for this and all children/fsChildren
    if (pathChange)
      this[_changePath](newPath)

    // clobbers anything at that path, resets all appropriate references
    this.root = parent.root
  }

  // Call this before changing path or updating the _root reference.
  // Removes the node from its root the metadata and inventory.
  [_delistFromMeta] () {
    const root = this.root
    if (!root.realpath || !this.path)
      return
    root.inventory.delete(this)
    root.tops.delete(this)
    if (root.meta)
      root.meta.delete(this.path)
    /* istanbul ignore next - should be impossible */
    debug(() => {
      if ([...root.inventory.values()].includes(this))
        throw new Error('failed to delist')
    })
  }

  // update this.path/realpath and the paths of all children/fsChildren
  [_changePath] (newPath) {
    // have to de-list before changing paths
    this[_delistFromMeta]()
    const oldPath = this.path
    this.path = newPath
    const namePattern = /(?:^|\/|\\)node_modules[\\/](@[^/\\]+[\\/][^\\/]+|[^\\/]+)$/
    const nameChange = newPath.match(namePattern)
    if (nameChange && this.name !== nameChange[1])
      this.name = nameChange[1].replace(/\\/g, '/')

    // if we move a link target, update link realpaths
    if (!this.isLink) {
      this.realpath = newPath
      for (const link of this.linksIn) {
        link[_delistFromMeta]()
        link.realpath = newPath
        link[_refreshLocation]()
      }
    }
    // if we move /x to /y, then a module at /x/a/b becomes /y/a/b
    for (const child of this.fsChildren)
      child[_changePath](resolve(newPath, relative(oldPath, child.path)))
    for (const [name, child] of this.children.entries())
      child[_changePath](resolve(newPath, 'node_modules', name))

    this[_refreshLocation]()
  }

  // Called whenever the root/parent is changed.
  // NB: need to remove from former root's meta/inventory and then update
  // this.path BEFORE calling this method!
  [_refreshLocation] () {
    const root = this.root
    const loc = relpath(root.realpath, this.path)

    this.location = loc

    root.inventory.add(this)
    if (root.meta)
      root.meta.add(this)
  }

  addEdgeOut (edge) {
    this.edgesOut.set(edge.name, edge)
  }

  addEdgeIn (edge) {
    this.edgesIn.add(edge)

    // try to get metadata from the yarn.lock file
    if (this.root.meta)
      this.root.meta.addEdge(edge)
  }

  [_reloadNamedEdges] (name, rootLoc = this.location) {
    const edge = this.edgesOut.get(name)
    // if we don't have an edge, do nothing, but keep descending
    const rootLocResolved = edge && edge.to &&
      edge.to.location === `${rootLoc}/node_modules/${edge.name}`
    const sameResolved = edge && this.resolve(name) === edge.to
    const recheck = rootLocResolved || !sameResolved
    if (edge && recheck)
      edge.reload(true)
    for (const c of this.children.values())
      c[_reloadNamedEdges](name, rootLoc)

    for (const c of this.fsChildren)
      c[_reloadNamedEdges](name, rootLoc)
  }

  get isLink () {
    return false
  }

  get target () {
    return null
  }

  set target (n) {
    debug(() => {
      throw Object.assign(new Error('cannot set target on non-Link Nodes'), {
        path: this.path,
      })
    })
  }

  get depth () {
    return this.isTop ? 0 : this.parent.depth + 1
  }

  get isTop () {
    return !this.parent
  }

  get top () {
    return this.isTop ? this : this.parent.top
  }

  get resolveParent () {
    return this.parent || this.fsParent
  }

  resolve (name) {
    const mine = this.children.get(name)
    if (mine)
      return mine
    const resolveParent = this.resolveParent
    if (resolveParent)
      return resolveParent.resolve(name)
    return null
  }

  inNodeModules () {
    const rp = this.realpath
    const name = this.name
    const scoped = name.charAt(0) === '@'
    const d = dirname(rp)
    const nm = scoped ? dirname(d) : d
    const dir = dirname(nm)
    const base = scoped ? `${basename(d)}/${basename(rp)}` : basename(rp)
    return base === name && basename(nm) === 'node_modules' ? dir : false
  }

  toJSON () {
    return printableTree(this)
  }

  [util.inspect.custom] () {
    return this.toJSON()
  }
}

module.exports = Node