0kvqd6ZB>4C|Jw16ng?OFaZ1@Dk#l`vf1^EO8
zdB6@HgtrsY+>6Hv!TuKqc`F3m&CUgB=j_A+aWuDZc1KFFfs_7u1xFVZmA@G~A$}?f
zsEp6c+=Wkom!Hqk5xUop-3X+H)&CyjpYBFzdAnHgX;>kg-QD289@gxC5d+=*dqL1f
za2g4DXSlngl@s!wyc8Q);kC50l(;P_D#|Y+&o3r=TUJ0oK}c4VUtU3hUkF;26A%>o
zi}s!q0%`69xB83L?r&N-#eYjHA?Idgj&yd@a&~t3%X$xNosrH6TW1#*IXRYVD&}xI
zC+PV)q}!iIwQ{rbu(DKib9Q9;p&SXje{uj3VUgPcf}-N0BI1J3;l%~S#Kq*r`DGQw
zg*im?5pwfrX+|4DoMU()h{!|*|t{l(IMEdlp|e*D{S!NcF4Z{-Bs-3|CP!Zr*9
z8L#d=`P*7vqbn1>&n!RtU;S=qT#~jM$vQmzE;@a9c-rnezm%%pNUCL4Z?Ol5^T|`4
zavjWJM`*UqD*=L#ruSwfkpg`dg+WwAgBG)AJMJnzGINiKeO4G0eE->l82+&`B7#WJ+>uienjpS%Cf@_)wnS8@Lt-(ODwuJ9`ZNaR1c#{VzAzli%c
zdieFczi910Q_uf9Ump#H3qJ49?XDdJEVgU0^-nuX>)vnly30Cxol4=jV*B(2W#5Q=
zZoyqw)~|Z99vkShEvcZ%Gul9NJXmfqlB33(l=X+jS8v;gb!7^)K4G4vKK&sm{RpAY
zXa|RLM59D+?B_$C1m;mhzR|W9s)a8;N9a~BS?g4pNK%w~aZQtaEyGUaQzjA|;I=b%
zPAV^cc(RIern)a4
zhI(|wR30|9!CyKW7Euzj4~l&9N>-|XEj&zlYZ+NWMQTM+Dn>Sa|7#Z3UzI};wxS)M
z5iocG$XR&=j~^L|KB-jVe&BB^c~JCVHeabiS7tMKFok0*ep-0e3Ze}V4cW;Q{F;|i
zS$nM{@47r;N7}?`S6OPDO>XN^gfMC4@e4?t?VN*ih6ihdqMrioS?+n3lvoDhiA<%h
z#p78S=zV$jMN1#_jm?eU)+~D8>Zr&$YG>4s?$aA0;vM3?Ri43*6b~&tl=Uo8va~MT
z<>ks>Gs>e#6&&0ooD@PIXPo9!ZnA&c5Gu>&{9rQ2$Q7}1n7}39u$DbUbe3Dtk+hdq
zDSK)7{F|d{*}-@89lT@N8WW)f4_Y3)=PQt3&`leqPLEq@F54On3Q{O`3Ek*NVbnPU
z3HRC!)K5#DyF3No?si6u5Okc#a@1b3r+N6zae!OadVQefsha>mB{P0x2xXtyPVg#Sd}jsP;8~gz
zYK^AUM8?3mAz{VD%2h0EqGie+}5cS
z>3_AuW9^jOkayxhI^GLWCW2BtRp*eX>kO{Zk+g}I{px$AXrD-ryTauP;u;cGAfLT-
zSD?0wa8lBaDV0a%bnCmP)=Y2PXe(WwysvAq@G|dvY)E8FQlK92Q))iT3C3DNZ8pT0
z^xBNvl&{jZjB>4M1k9s#@C})~){+R}@JjXYof(EmM3e)0b|n*jq=LwAqt8G<_*=l$
zy-rc})-+~(40YP90onyuio3BUUB~A!jMgE_o8VAUF4miGu-@zM8IP`hju<5zye^-k
zeG=bdFRG?$R6^-YRJk`*b_GQE*?9u{$k?j=qd=I`1^c7fZWgmLBpnK;9ohqXv^hkb
zm1KwQ+FCun1mc|jpv1JDF4(}xQXT}J7S8V~pYc=sgE@#MxS6((3x$#85G*RZy#4l=
z_D$T#lY#h=bjIUB>=5fGmcizywk5(jI-HuY;=A^eXQ-7|3Q$rg5^qkMVqV3;15L#r
z`WA&tUhStHJSIO3&?$Tri=9^B=*bex>D-yE$OcCG)STFdTs#_K)2-3^B#Lp6yEE{%
zttT?<)JM-#X610Il@4oS&l~UWn@J4I5@y7YWV)bF9=ysF-`S(Lrmv$F=$j>r?AAMV
z`~IQ~2Rq(1A6u*LH=mlaIty;}AVt{{bG2}8N)veY{ON#G|
zpCH<$CHM1gPc&9Ln={MI->rfzD5VHlM%r%=#e?)i9xR0NdRcKHzH@*(KBAU<*+aze
z@&F$b-7qY4u%U)i?v875tGFCGyuzB@?88-7t~9C%6lLP2+0-`*xO)4`8BY>llBHIA
zFSj;Le1vbJ!xhIx?^M@|-I+0B9W%|Uq{EMtz9bVlw|0=2pF9N1%D?`xYb%W)s_Z?Q
zw5Muirs2MfXOsUIiKmDb;NInp-@WzmBTp{(Jxk;B6XG~MXQ`5vo8EAnRpQlzXQ9a9
z!3Rk~>%iuQR)06kG5g-AN+{?GTIg`}Q)j6CSCmmTXr=6K@q)-$Z4-Hk!~l=eM*6Xu
zZMowHW>@B!2C)oA6S3x$r-&yGYB`m(_>loreKFC&PRUA^`O%lRRiq;Z?rjb`*v;pJ
zt*!1J6CHjRbv0%9u(Ombf}TG=g3g9$q{;QCE~FkBT5$%tc&mFKUJO|@JJ=^+9tWYV
zcHn;4f)12eX~~PW^mXNZA!93zoPJb#1>;;8)P`4cE$uaz+&t~!@CAiw|EQfL^B}3f
zF9jg>mND)ePKe>1#%q514fPsH#9t%qzh?rglEk>QZ_Q#S3MmsyxInBQwrxZ*1USKb)hd<8YTnSfa_5l-8xQ*+|7iP4f1;m58I6Nje@
z!&YMl^N!J_Ma#1yl5UKug3ZIv5kU?mnZh*qk+(1PrR;?b)S+&<0enT%apJ5
zFK^?Xjd`uq9H+aqjz+>8{MtD~I$=ZOhM?wI1ceCYe#08)QhFz`XJtsE)mbGH?Wl$L
z7*BNiovp{)?b03AGKJ^gH_UwqA#NF6R#rSiEk{w#L^3hK87po#Sznl&R;~ej;^gI%
zH3rOHhRpJ3T;v#a)rZ)Je9_xt^IaaE28iEEX(-3!BnwHr9~!E08R{0!6!W&V+>ZAmd&NGX)_GdboJJOVB122jrQcpF>VkQf(3Jxylq^JlpORzG5QuFIt@&2KB@
zJ(O@4thYJs1F_F@pwK#lXkYL+E0Mw-EWpqqII@#fdHF2&Bt>7f3CV;uXY77+MXkoC
zK4krCRqI{Gg1!citzw11$)V@~A?Yf1tjX~+(xS5a=+pQ?4T{n|DpNAtb>N9o_U`H?
zxTM>u_tz#*!{9HSq#VoA_@s5c`eZcrcV>o*QtAy@H`*#U`{VKPBc~|(>Y_uP3Y5NH
zDU8hsXm~Gz{CHEuFv&vli0+?0^Lwi4IRkjY%4SG~_2VD~-|71+UD%0CN^hmBgAt=)
zewPa366^2i2$M4ymby|i9rn*6a*<(|9%~L&wsVi1s2K>|ZknlXU8bsse4g3V@Y%E4
z-n$)|rxS!HW5DlKQ`S17p3^cN2Ijhi!Gib|3z7kDXm~kbaN!Jf$JLbBV{RlV09AEH
zpZeBqUaDG?tGu)^O+6*A>*#p)yr`K`_3R9daq)d)+u+I#VXIqB7R3$JqwwyS9Jhn~
zjJipxGN|G_+U(#>92Ycw4rAt$&hAFTI;o$leDtaOy+aI?|P%Z*CS
zB}?tO5t#@h?;VSBwK0+Kv+=t>TF_A@KC?t+T+I*B#8Q)2uX(Ju
zt7e{|IKArp0#W`xSZCnr$%P-R9p{qyFtO4Awwo4xEpA4X3lN
zdb=ZLd;C%Vb7M5mp|;G4lOIX^%4^CgeRsng-quQ&FhLfx50x(2I}VG*n&CIIwm)Z_
z{KjXS+EW*Rtf*NVcC2Y+4iuFRRRUMCV2HEC@D_!GEAbVRI#z+In~JHW(Vtc*ii7vB
z9&RNlS2fraH^KrICe}T*OIsc@x0^9UT8^a-%2JfRVxE4+)47e_ps(nN0;Xw4GM-Vei=p_
z%18j{E+aje{#=GW@Xj2_v?UwnyIm{Ia;!!ZStJ1XV@j)A*BVUqF0Za86rimDv`o*c
z=dZOk5!-v&aX4F)F3dK@RWGoU#PNhLPzyaMO;M^>8bifb>{*~>sajn%6ghf0#3@fW
zAR|>5usIlRFL7*il(_enbC7j7OaRuwG~%RU&kP`G?t3|e@6-FAH;1UQlJbTGEOFAM
zjw|Jk&immjd(P`NTKV;2gEOz#$L#HT2>^(v>icTKIsWCw={aX@P~+za)>3?Yt&%`Flm`>GwnnfRwZA4mCH0QH?>inQ6e#or?<}UtLS9g
z!{aKumsX3J!tLGNpF+AS1|J2rW&F}I^6>p1Eo1-D(P_si<7sktg|2r;#2^j(SiN1;
z6YebQf!*XT&v(a|N#@$Xi@i4Hf;|m>6=Hk6r?8VSg9_3&%S(iuYX+YW42QOa4o%WW
zd|S_@N~B2eo%ZKkW~V=u~H>S$Fw2t>iEi6CblqEt7CkUl4B`YEIKDFpY
zz#|;^vV=*`awq7B4fskysB)m(Ok(uAjb}2vG9t7M?nf|A%)Dr
ztE%yxDbI4AQGcFtRlmE@T6U>}Vr2EcJq?hM$%taq3ca~qwymHpz?1lf-`W5kW<)qJ
zdJeF^o`S!6VL}-6w~m!GQWL}+2$FfImGkKIP%*%OK}C+gK!pvcv~zI#h}d`&Zt*^a
zo6q7$#S1R8nHm)o>w?JlF%i3T^KxyZHk+nZ35dFhK?GYXt%U*MI9MfAAF%{E35Z
zX5|IIU{qJmR~C#2`izX96>jISe9T~Mmu0wQ>C0Ah52UQA=END~BCkgH{W%Twsg-il
zI+QSVcf&Dcj4QyMZv__;%6dNkF#+M`%5SXKU=1~5AaLc5!KIZ_`>WAXl+xTVl#|RS
z@t)D$P@b%zF<2kQ{Mq_|G01skF@&E4QZ!e7o_Hnnp}J(RXh*1sSs0wT18>CXt~~|7
zsG0p?JCC7R4hDUV2VwXEMLab9O1Gu#>KL&d4qWlw|>3KC=>ab>^T*Vw@5Dvyf*0YD0(l5oL(wz-)k2H=yMsg79uKt
zwHCpIPUd7CH<%2|rBVl}0Tb0}lFhq|=sajhpgx7nkRW+=kR4S7S4KP9IRXWs$bR$;
zPyV-OprKgTOORRg@|b>9ksTCt?wtav7j%jTP-l>QbX@WxnxkfhPB0JnX-u$CA9L8M
zHGp5n(s??c)V8DEHjsBu)3n3yp_2y;&YYn>`h!c>R~z_+uY5hVRtPw2Q#Yw8C%p#%icDkL6PMXS?@wH)LTUh(4_hS`{p-
zr4&sq1`Itk8h!QErL3}Kx5B*&X9U>CChU5_q&T`|2TKMlF`96xj{PHfvp{}S&4fCP
zET{6!S#B|=>1SJ4QHL2yU#}OHP`_c;FQKSs3T_eLbac@VJp?a<|;Qqe0
zjJ@bL7PH%-&cQpSkh7vQuB9&$e5%i`bJbrSj=@b=hyX)~H`6|R>or?Ws`B|2=5Lr>
zpLw}4xU|gOp_6_Pb*yhdf2-*+uk0W@3ZudyXxgt<46EEdC{!zX+L3WY277{Qb~IP<
z(gOZv7GOj-Dh;(_bq&|FRdzYZG1D}y@Y`sAJ075tc{wq-zW`Dfz#HPjB_(yqf$f9@
z0|?uKEd?J9@Xk>8yx|Mmc>&!Snv{C8W4bko+HwqBAxIDz0>qmV_j~_$_8E}&tTorqq&Q*YYqIn
zYYq2|tXxpHW^=%fKq(ItBYoLoZ}+V;`T
zC-{zURz5G`QB<7Bwt5E}elWX~?S3(quWy|T#9`V3o^m-(6#>S6
zi|3}FiJs`2W0bzy4Zc@6zWLH|QK!(nkOP3Y8j>^ATQPg)yO8w#ig<;&s)XzE*?Jf~
zxpJw?&7Y0Cf%8dS;qL3j8q*kP3U7QuJ8)l2_e{rVk$V&FHBD>YcfWgWJcHD$rbNk3$AQj`y23cVG
zWrV2Hc@k*X^!t7)5c71@_|4TMD4bq#nVd0QPmceU-5e}}=
z0SdsCU#kTR&A?H~TH)hgA8^K{
zup*pP0%LUpfHCXsqM;qO_O^X7LfW%Z+l7?QwQhsxy%YmjUCXnebI05%tfz=YC#~?F
za~ANT+(55-*`p-AI6eCE37YwNO-Mb*j=HuDSugSq)+^gObH^HwmNRG{{$?+We)%!w
zEI#ppzL;jfKLDJ-V(mz)Gd3`fY;n-zkOch$uwguqXr*g4h{46)fC1&+rs#WRL%b`o
zY>{>C2db%j$Ujp~V*KhZ;v^eB$%!VoxS7)!J*Z4U9e%@HzN?F6v;-$rdL$
zQhX0V4w##nlHh1TlGqG6{n-(N=^BOVCkDG&D?C~^_zGv9#?f6b3F;!!AEmgXs!|!eW&kmK)gabz4456L!
zwqX99vlAYrAkeAHeXVB7?4|Wb2IyD!#NM8$FFI+5_q5Gs8mJ#O)xIN*F`oi3#`E>&
z#2@njvhfH1IW?wmgJ2AOoz?HFn&YD7ZQ|}VP~gT~v>7f5UcCs~$8wEN{Y-T-Zz^}L
zuh5Sa-$f9E3tE_x;x7D{6uAqOd$^pupx)(Jy)=?`7Xcs=jrH_>UkM}(lux(3TxX_~
z_aYTLSt|igAl-xCgYm;Z4tcGn*6xglRr7&yZcop!bOR9AG{v~GgcpFf4!Sd_T$#&Dw*QoqaeotqG2jp+=t)vim+_bDcQPGES4UZ5Bzk_Esy>HLT<&-M#?VfembeX2e*SJT
z4^`E5C;E{zRRjTGu-Z&}5Q}xx48Q+5HlrbLmv;<1nw>vaLOk|y9K6r%{1dtqK+r`2
zj5{OJM$)QzT$4L@ZY5m;$1}!UtAJdWSrT>4XIhCUtAgii1j0;%7Wkz1Z+*jo47JJN
zwbW3G7S~b^@3H^IS&>#p*0EkYUZ}Pu|BYK*G)-&AKC~`GkKID+4ln@8uVkis{GERk
z422pRDc^4=H-~&ysa8dYLllz;fauNDK`fIlNLNC-RCDgzU^=vyvR>0g-EqXz4VZI3
zbvZA?SWB$UriRGW`qV*vT28tyK8uLMHS_QIcM*V^hmF}bTU^x%?@?HocptHJoePHh
zG4=T!AeJ5iz@i$5VuaFt8L5K0NwDXOW<*6=q*2&_9wDIintpcj6R|kvQUS!WN6*u-
z@U&k6z5M5f8$-e9JJ1_dL(LZneB-?dv+#}u7DI+H09AnI)kB01e;NHvHv4~(lJ7A
zSr%KD=ofaSL#E4S;O!k()=GnSZ0sDMYv_0S{En>3lecF2F~5@xOplLvPQ@JOP!%F@
zoGS=)S^wpiN!{qN+ht~UX=*T(D}##SRx|BsBnqR%A^7A204XwW4wtptI-WjP01!nF
zV24H8pW`=iUod`44eZU|!>kK*?r71qK&;tQ_L)g1bmBX;7_ToaESREE+Zb(S1K5=T
z8i=?iW_toVkwckC{PFN5EIw`_z&m75avi*vHl@MrazZgOuy!_Bq=>|O$x!vxSoe)T
z0>X?2?0Nw3-73yoZ;wM^?tyx(T1d66Im8&qLJWo>VGg2M0>prHvzP>D1>vuXVj5bU
zUvl!E(7H|6jvHM8bTsFOb?XLyW0HaW{(z!%{Z){8Y%|L)0&3A;CtN2*C7n#CJSQY0
zOv>=fpdbd`j}o<(6Gc(h6lmtj*lKtm|ibroh%QL_Ev>4=NQL-!n@YWXFnB7j_AG#{>=QMhW
zdA|*W&uR2&l*7=U8+LykKB+ra?lpFyC8HDo;}cQg$)|w{BgH@F%IY#}!8(zr$qr)!
zdS|Ki@F&DfOBrNc>fDY3IalT{22uDc)){or1ZJpGbExdW6#KGe`Vc|)yJeMcN)cx^xJrGGfNNBMK
zM$rOD;KKY!l$`@aP-KkSPk?RCW!=bzwhw*sn%&0Yz=p2AKUXGkD5(q*
sP!ucps3V{^8cW7WfI#v`F+z_MpS<8|i^b{}<`S*bHs(-P
zteX+(HkOcJeSr$=xlGXJinLzDLLth&Kz>gzI4~F$C~$ZYS7i#EA<}DNFlHJxvHCK!
zq5=m=IH)Fx&bf}VmP_lgDMEpc9?)1GJhWB2M$mQ1?moz6F+e8*#(Vrijr_6gXIaef
zjYA99Lcohp8lTQ0DCHLZK&}9QuE%0ZjtlGNzY*{%S{#TVwt86i+ZuMYoU{eb
z#)trlq877k^Auiv5dO&Xj>I5f!u`jde&KO9f9VcP+L`bSb!j#5*!5&9-GbJpS_wut
zL^JYI7U7urZWAeihA7ZL?2mHu=q(SU;cK<{Bo$NDM*+~}>_i%wt`+|1`z<2fbdB?i
z&XeyRzvHI`FU7a&6Or7|2_I_CM*V1o7kx!@;2=b9J+Ee~(2Ykq*C$1TMi#%`0(>wd
z3dY!rhEYfoK(Z)M6}(d|xRu&5PzEyo>mi_*nXpVgt?&kfxr7He(Uxte4?ohgKz^!P
zl2Hhp*k}Alo7yj#5={lMbOP=ehczytlv+DL76R{ByktrQy3nmcsc$sEJF$G=(uA^A
zavVN`(a2}e2B9e;oVnGaIC&17faIE}Zzw)=zrKqTGVS}tbW*ImwF}@JOxhP|jKC%F
zUz&6}Ylf$}CcA+77e5Uzsbjhzca8ryJz#n48sN?sy2=@^=cbzl#|Nek+HwY#Ij|6{;EtnMn>5~^3_?j|usez*SXjmlq*%sG1+l
z-hh_67df3O2b|)a2nb$5q-b7iM$Prl3}6M{)5?~QtL3q;_-8uscpKyqFCL&!J(nmg
zdmt^ebx79tJ}q?*Jg*&Sk0cV<(_!(7mc_gKnRKAm@LRGjAEGeI9P<;#d630P^1!f9
zBh(vl?K8-kLFQ1p5J3**wbs?}{#ar4bhO39qwF!7JiuTy9>B(XHbpqj50$vQgr*n-
zpX?V?W0%w7y>}}D@tqqFh;$8K;6DXnc2rI*hZq$0?A4M4O5f84gkG*ahT0Z=PAZj9
zREa{FYR&D)L|OR9BXk##sb*@b1wWI3yNn(v*{Xqe+-lhdK(Dua0fQg?P4pGbA`nDh
zz-K@HD2y)0fV@Y!^WgU4D#Hxr
zc;Z7muz&O69b3XMmqzkze*iOs_JQh#K^qb3GE{Q!kPK`fo;kp#(PkX`U%?3h2Hekp
zF{ZJ`H{~&YuA??VvpNq&lJoI;MxYPT!m!7aL2L6*SdESw-jMdhdzJ1*Qn#{!s9)h~as!ad0K%@fDpf90I<4(3eq`@}GcpOMwk}t)^u{)$dg$b=-
zI%yHKvk+dP2C=vSSPZn8`P2BZ;UIX(ZP$5VvbahcWL$FDIzR
z8<0Yy1G!mmU4gIHr4rRKBA`tFIc3!LOl4M@>ezYkcns2?>+i@hAD01imLq{41sL(a
zvEi|en^K}cQ6veV@*c(PhbVvl
z1j}B4%FrVcee%%NRRzCp%m4zaBDcnvMyD0L(ptXNKrBmvLj_RQF2kntiq-nh0iJTx
z#3-D8Q^!>V2NiTdD;@jyvx%_*|kRtWKpG%IZ+;g${P-g=xg>)s
zMpIibKaDC@dJf4y3xHuBZ@_8Z77eh}ts9_>($#kHGFb>pop1VMlSlS-ju8m$M&b$P
zp7o(s;^`%FZSqB`$#-XbR6wTkA(N3YM(*~(AbmiU13*1HvPL(}14f|H*HK(@zWZVU
zt8-g#A?}jEq(ibpV$sZwnd#@iC!WzQAXgRkHp(OfoCmiTV*(q%0I6a>HhfD6P&6^f
z4(`f%4kaHA7=N1r8Vn+ll$G34K0SSK;0$q$1|q>UX!YgEfcv*bkR$Mnt3@g%@{9`@
zZj~Sk7+&*1r&sF;0Iu@#A)vr(idnxZ`=5Lk`(OBc6{zU{@~dH>r2pV+-dUp^217t6nVH5`QcetM1u@7n`oi#fYSDd_)Q+*44MFP1fX{69{$*5UvF
literal 0
HcmV?d00001
diff --git a/playground/vue-legacy/env.d.ts b/playground/vue-legacy/env.d.ts
new file mode 100644
index 00000000000000..31dca6bb40c906
--- /dev/null
+++ b/playground/vue-legacy/env.d.ts
@@ -0,0 +1 @@
+declare module '*.png'
diff --git a/playground/vue-legacy/index.html b/playground/vue-legacy/index.html
new file mode 100644
index 00000000000000..0f7b79435ed47d
--- /dev/null
+++ b/playground/vue-legacy/index.html
@@ -0,0 +1,7 @@
+
+
diff --git a/playground/vue-legacy/inline.css b/playground/vue-legacy/inline.css
new file mode 100644
index 00000000000000..2207a25763ca6d
--- /dev/null
+++ b/playground/vue-legacy/inline.css
@@ -0,0 +1,3 @@
+.inline-css {
+ color: #0088ff;
+}
diff --git a/playground/vue-legacy/module.vue b/playground/vue-legacy/module.vue
new file mode 100644
index 00000000000000..10c7b42e4c4215
--- /dev/null
+++ b/playground/vue-legacy/module.vue
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/playground/vue-legacy/package.json b/playground/vue-legacy/package.json
new file mode 100644
index 00000000000000..201a5ae47bb293
--- /dev/null
+++ b/playground/vue-legacy/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "test-vue-legacy",
+ "private": true,
+ "version": "0.0.0",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "debug": "node --inspect-brk ../../packages/vite/bin/vite",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "vue": "^3.2.37"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-vue": "workspace:*",
+ "@vitejs/plugin-legacy": "workspace:*"
+ }
+}
diff --git a/playground/vue-legacy/vite.config.ts b/playground/vue-legacy/vite.config.ts
new file mode 100644
index 00000000000000..5bb2f0efa06f53
--- /dev/null
+++ b/playground/vue-legacy/vite.config.ts
@@ -0,0 +1,35 @@
+import path from 'node:path'
+import fs from 'node:fs'
+import { defineConfig } from 'vite'
+import vuePlugin from '@vitejs/plugin-vue'
+import legacyPlugin from '@vitejs/plugin-legacy'
+
+export default defineConfig({
+ base: '',
+ resolve: {
+ alias: {
+ '@': __dirname
+ }
+ },
+ plugins: [
+ legacyPlugin({
+ targets: ['defaults', 'not IE 11', 'chrome > 48']
+ }),
+ vuePlugin()
+ ],
+ build: {
+ minify: false
+ },
+ // special test only hook
+ // for tests, remove `
+
+test elements below should show circles and their url
+
+
diff --git a/playground/assets-sanitize/index.js b/playground/assets-sanitize/index.js
new file mode 100644
index 00000000000000..bac3b3b83e6b1d
--- /dev/null
+++ b/playground/assets-sanitize/index.js
@@ -0,0 +1,9 @@
+import plusCircle from './+circle.svg'
+import underscoreCircle from './_circle.svg'
+function setData(classname, file) {
+ const el = document.body.querySelector(classname)
+ el.style.backgroundImage = `url(${file})`
+ el.textContent = file
+}
+setData('.plus-circle', plusCircle)
+setData('.underscore-circle', underscoreCircle)
diff --git a/playground/assets-sanitize/package.json b/playground/assets-sanitize/package.json
new file mode 100644
index 00000000000000..3ade78a2bd33fe
--- /dev/null
+++ b/playground/assets-sanitize/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "test-assets-sanitize",
+ "private": true,
+ "version": "0.0.0",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "debug": "node --inspect-brk ../../packages/vite/bin/vite",
+ "preview": "vite preview"
+ }
+}
diff --git a/playground/assets-sanitize/vite.config.js b/playground/assets-sanitize/vite.config.js
new file mode 100644
index 00000000000000..0e365a95383833
--- /dev/null
+++ b/playground/assets-sanitize/vite.config.js
@@ -0,0 +1,11 @@
+const { defineConfig } = require('vite')
+
+module.exports = defineConfig({
+ build: {
+ //speed up build
+ minify: false,
+ target: 'esnext',
+ assetsInlineLimit: 0,
+ manifest: true
+ }
+})
From eec38860670a84b17d839500d812b27f61ebdf79 Mon Sep 17 00:00:00 2001
From: Bjorn Lu
Date: Fri, 19 Aug 2022 20:55:04 +0800
Subject: [PATCH 093/100] fix: handle resolve optional peer deps (#9321)
---
.../src/node/optimizer/esbuildDepPlugin.ts | 24 ++++++++++++-
packages/vite/src/node/plugins/resolve.ts | 35 +++++++++++++++++++
packages/vite/src/node/utils.ts | 8 ++++-
.../__tests__/optimize-deps.spec.ts | 13 +++++++
.../dep-with-optional-peer-dep/index.js | 7 ++++
.../dep-with-optional-peer-dep/package.json | 15 ++++++++
playground/optimize-deps/index.html | 10 ++++++
playground/optimize-deps/package.json | 1 +
pnpm-lock.yaml | 20 +++++++++++
9 files changed, 131 insertions(+), 2 deletions(-)
create mode 100644 playground/optimize-deps/dep-with-optional-peer-dep/index.js
create mode 100644 playground/optimize-deps/dep-with-optional-peer-dep/package.json
diff --git a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts
index 57e67c2b47a166..04c978130f256c 100644
--- a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts
+++ b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts
@@ -12,7 +12,7 @@ import {
moduleListContains,
normalizePath
} from '../utils'
-import { browserExternalId } from '../plugins/resolve'
+import { browserExternalId, optionalPeerDepId } from '../plugins/resolve'
import type { ExportsData } from '.'
const externalWithConversionNamespace =
@@ -93,6 +93,12 @@ export function esbuildDepPlugin(
namespace: 'browser-external'
}
}
+ if (resolved.startsWith(optionalPeerDepId)) {
+ return {
+ path: resolved,
+ namespace: 'optional-peer-dep'
+ }
+ }
if (ssr && isBuiltin(resolved)) {
return
}
@@ -279,6 +285,22 @@ module.exports = Object.create(new Proxy({}, {
}
)
+ build.onLoad(
+ { filter: /.*/, namespace: 'optional-peer-dep' },
+ ({ path }) => {
+ if (config.isProduction) {
+ return {
+ contents: 'module.exports = {}'
+ }
+ } else {
+ const [, peerDep, parentDep] = path.split(':')
+ return {
+ contents: `throw new Error(\`Could not resolve "${peerDep}" imported by "${parentDep}". Is it installed?\`)`
+ }
+ }
+ }
+ )
+
// yarn 2 pnp compat
if (isRunningWithYarnPnp) {
build.onResolve(
diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts
index 13ac18ae384371..f33105d265a27a 100644
--- a/packages/vite/src/node/plugins/resolve.ts
+++ b/packages/vite/src/node/plugins/resolve.ts
@@ -30,6 +30,7 @@ import {
isPossibleTsOutput,
isTsRequest,
isWindows,
+ lookupFile,
nestedResolveFrom,
normalizePath,
resolveFrom,
@@ -44,6 +45,8 @@ import { loadPackageData, resolvePackageData } from '../packages'
// special id for paths marked with browser: false
// https://github.com/defunctzombie/package-browser-field-spec#ignore-a-module
export const browserExternalId = '__vite-browser-external'
+// special id for packages that are optional peer deps
+export const optionalPeerDepId = '__vite-optional-peer-dep'
const isDebug = process.env.DEBUG
const debug = createDebugger('vite:resolve-details', {
@@ -365,6 +368,14 @@ export default new Proxy({}, {
})`
}
}
+ if (id.startsWith(optionalPeerDepId)) {
+ if (isProduction) {
+ return `export default {}`
+ } else {
+ const [, peerDep, parentDep] = id.split(':')
+ return `throw new Error(\`Could not resolve "${peerDep}" imported by "${parentDep}". Is it installed?\`)`
+ }
+ }
}
}
}
@@ -618,6 +629,30 @@ export function tryNodeResolve(
})!
if (!pkg) {
+ // if import can't be found, check if it's an optional peer dep.
+ // if so, we can resolve to a special id that errors only when imported.
+ if (
+ basedir !== root && // root has no peer dep
+ !isBuiltin(id) &&
+ !id.includes('\0') &&
+ bareImportRE.test(id)
+ ) {
+ // find package.json with `name` as main
+ const mainPackageJson = lookupFile(basedir, ['package.json'], {
+ predicate: (content) => !!JSON.parse(content).name
+ })
+ if (mainPackageJson) {
+ const mainPkg = JSON.parse(mainPackageJson)
+ if (
+ mainPkg.peerDependencies?.[id] &&
+ mainPkg.peerDependenciesMeta?.[id]?.optional
+ ) {
+ return {
+ id: `${optionalPeerDepId}:${id}:${mainPkg.name}`
+ }
+ }
+ }
+ }
return
}
diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts
index 85f85adf3cf550..0f681ec8f7378f 100644
--- a/packages/vite/src/node/utils.ts
+++ b/packages/vite/src/node/utils.ts
@@ -390,6 +390,7 @@ export function isDefined(value: T | undefined | null): value is T {
interface LookupFileOptions {
pathOnly?: boolean
rootDir?: string
+ predicate?: (file: string) => boolean
}
export function lookupFile(
@@ -400,7 +401,12 @@ export function lookupFile(
for (const format of formats) {
const fullPath = path.join(dir, format)
if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
- return options?.pathOnly ? fullPath : fs.readFileSync(fullPath, 'utf-8')
+ const result = options?.pathOnly
+ ? fullPath
+ : fs.readFileSync(fullPath, 'utf-8')
+ if (!options?.predicate || options.predicate(result)) {
+ return result
+ }
}
}
const parentDir = path.dirname(dir)
diff --git a/playground/optimize-deps/__tests__/optimize-deps.spec.ts b/playground/optimize-deps/__tests__/optimize-deps.spec.ts
index df0c080f09ab47..fd717b0401499b 100644
--- a/playground/optimize-deps/__tests__/optimize-deps.spec.ts
+++ b/playground/optimize-deps/__tests__/optimize-deps.spec.ts
@@ -88,6 +88,19 @@ test('dep with dynamic import', async () => {
)
})
+test('dep with optional peer dep', async () => {
+ expect(await page.textContent('.dep-with-optional-peer-dep')).toMatch(
+ `[success]`
+ )
+ if (isServe) {
+ expect(browserErrors.map((error) => error.message)).toEqual(
+ expect.arrayContaining([
+ 'Could not resolve "foobar" imported by "dep-with-optional-peer-dep". Is it installed?'
+ ])
+ )
+ }
+})
+
test('dep with css import', async () => {
expect(await getColor('.dep-linked-include')).toBe('red')
})
diff --git a/playground/optimize-deps/dep-with-optional-peer-dep/index.js b/playground/optimize-deps/dep-with-optional-peer-dep/index.js
new file mode 100644
index 00000000000000..bce89ca18f3ad7
--- /dev/null
+++ b/playground/optimize-deps/dep-with-optional-peer-dep/index.js
@@ -0,0 +1,7 @@
+export function callItself() {
+ return '[success]'
+}
+
+export async function callPeerDep() {
+ return await import('foobar')
+}
diff --git a/playground/optimize-deps/dep-with-optional-peer-dep/package.json b/playground/optimize-deps/dep-with-optional-peer-dep/package.json
new file mode 100644
index 00000000000000..bf43db6b7919d9
--- /dev/null
+++ b/playground/optimize-deps/dep-with-optional-peer-dep/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "dep-with-optional-peer-dep",
+ "private": true,
+ "version": "0.0.0",
+ "main": "index.js",
+ "type": "module",
+ "peerDependencies": {
+ "foobar": "0.0.0"
+ },
+ "peerDependenciesMeta": {
+ "foobar": {
+ "optional": true
+ }
+ }
+}
diff --git a/playground/optimize-deps/index.html b/playground/optimize-deps/index.html
index 7b0c43e82fdcbc..fe507e9ba568f4 100644
--- a/playground/optimize-deps/index.html
+++ b/playground/optimize-deps/index.html
@@ -59,6 +59,9 @@
Import from dependency with dynamic import
+Import from dependency with optional peer dep
+
+
Dep w/ special file format supported via plugins
@@ -152,6 +155,13 @@ Flatten Id
text('.reused-variable-names', reusedName)
+
+