#&pZWUT$TJ|n z7JuXVHJ@kN-?*Lu`L*~P*RT0J)BeWw49Ks=-?)Cw=b835u4h1gE&j&!Yd+7kzi~YS z@@w%ou3z(cru~iU8IWI#zj6JV&ok|BT+e{~TKtXc*LJqT$gjoUxPHy&nf5oXXFz@}{>JrdKF_qj zaXka_Yw?@Cz|PK1Jt+gAAv5LlK9Hj$*s9hzz#^W8t{`7-8&qs8@vZvEjkp;Ne0H zEZ1Q5U~ABGQM%!s*VrHn8^CRfK`v02o{yN4++UWTU(c5;Xl#i!L-yj~<6g=l_tn>l zB}S5*A2ZG7u2yVS()VUZ3vNjh>gzUaxQGJ^=)1H&J!>{>>Bt VeJqDd5V0-5C*lcU=sS@dq7%B?4YYr-xU}=BbIj4Gb3IWsdnWh2Kn> z{X#Hv1_*BGRR*T>HYJ8RCMM3H7qPaT96QANm5{YgB?D(Da^9egsEwx2K?*l%ivYvx zu#c)l{s1uIvtBGlw4hM;jj5~Ca+#Ikg`8aTuBY}V$u#OMb~Rz57$m&yyp2iNQDrLL zRfVTweIVoF-p!GCWd>dbN$OqQUAR|I49J(u;TMFX1fNLCxsV7j(wv ;Ku6F=8&bbd$cLH=RN-KVX@9J3j Gx6ufO#B+7LM7KvDIISZvF+pm}n9Qm894^x5zI5R%t%;lGIID*u6N>!b?ilM!* z6jb=x!Ca@Z; Fd*L8GeUta15ud9kL@;W3TcRYzV)>CMa-|IcwG_lMt7>Rj|;L~JDi z)9GmxJVcmS$A4=MX0*Wc11(sVgc!L>m=wC9EZ`(F7VBgR-$W-HVglPIQJJ3G=irj? zvwu`VFg{(DPK)*4-8XB5R&KVO{q^R8_FQwb&mn(=2vIr`Cg2Dxlk@g1Q!jYko3Ldq za_>wlvrSki;>lnvK?E^;Gb--fH?fT}(e|*htpFWdv~JOLC8p)tegYMPK<2LRkR72Y zLIkzs6PfQEU9y?h{1gPE5~K%tYaSH>BobTY99_Kfh6Yt^E{6U9u9s%ua|K}yVGAwA zM&%n9YhaGipa0(LhZLHykIH2BJ9Nl31~F66yBo+Tn*h??gkCLhCFGB-@7!?hBh;y+ zdkLv<)Ml;~;H>527K=Xd;wU(?kzq|bE8uX6!hx%uPcRGZ2K5MPP#N2|nd}UA1%BIC zdgFo_^Uzd-_-Ff!(qKHWBhupWI9#X*dHCb+-V#n3JV<$~d=5?})7%AmgpN Y`M1JSd{;2TmTNK-@u7VQ93*H z6 UUt!M z3nd%J0H(V5WBD1jn`2;+9fVm)ehN}JGe5 $1@nb&aak8C=DDLX4rNl}WD?7#>t1 zy9I&)GNN1*V0@*JA%Tz)GjKjGF2voKI27*wpR#MM+gU+OW|B5i;KZLrv+IXINAIEf z=_GeLfFF*?nHg@G#N=`(;@dk2l%#iDC#%DRl$Sr2?2W>-Z>1(Y(cr2JkOgFcFWY<~ zXSo)#tm6Y_T4^3Cdb5^Ig7 iYQUyY17>tq7N zbxJXSuY`_5P81vcPI)(x1g-6Ta~sh0-UDp|&86er@;JDxLiW~96>C{eDQuF&Wh?Q( z20NI H|C^fsNGgQ^Lti &0(&=g47$&Cm$LEG04s9uW6k+6-P(?>1r1 zd1dYoU`Is*;eEjI>4(50zOVvs%kVssOT4+NA+3q>v53qm6 Uh3?Q}W)L*FI=SQAXN_u`6?sa0rjVw$?_AJPOf$M~5 zV|;fciNXg)%mfBh*zP~@ER!+xATZl=*ME=5(Sos}A)f5d#lt=dLIoXE^v3JMkV48w zU#ZU*gOA$2iZtx-d~w^&ef{MY3z?N+=g2^9*;ooFp_6?A>0eAcBkV_QT8l(7J}$?; zY}{u&WWSPzyv;qti_PbFi{@zu%OSSx{dp 0En7EXSD~ zgWymPcN5S-NlNn<3!93rbizuRQr{~2uc{3M6EddUnv9wBozzgwr3fLl_{f7kqD&uu z^gWhahO2GxMe!h0IEg@zxr?XCe>JcXhC7>&J!FQ~Z|O=1L++z1DaZbVYq44NB!$6) zRcI$X+kVNjHD9f|&?&DVSA|dz(V2oQV&&)B(b3O;?HNv0*@e%fNNQ%T8)^(qL9AL- z^_X9ImVhZAM<)_M4o-xPovRp-6(#tU5d)R#lPY{=&g8(s!lJ%3PoS`dim9&w4zkIl z$WBMPsRs_J&Q?_s4VN~pt!9z|s3Di!TV385kMfwD>1kFDWTAz)QBmJmMZA?xewe{; z-BfGM2Xp0U0`F1*GAm}JP5+E9Z|Z(xw~n5#I}<<3%q25*g;*qGqLq$^rVcz@GHJYy zU56exWhcd8xTTj%WFtYIwI%&{Jt*Lx+@QAs`zPL|$dKLCLRb(}Cc=u{+UPYZec^ll z)icnq9vJwe(O2aJ82H{w>woon&C!UQQ|O|rc@o#glU5Mm&@sk-8JfcR^&Z+KP$&6~ zD5;rS#Am0=ccmz5u>DGRu(Q)oE7|(&6fqar_3X3^x219v)+LTg*d%xb(^xxIQq={s zHIX=NZ;3LBet0^0G7K~8K~YuShfi+c6o{iynD3-v_{*@RUkTUg3%-%06q4E(Xs7c@ zk3JV0%s$^q+Oh_lK8Fm|?(Zwvy{NW70Tp}$Z-{UXpGGUZGOd19yqEojVi+eOXsKe1 zL?YE^-)!0wD0KJ%)^_|rL `b9n1<191#QzE;*K ;w6Pbv%VwFE6AbU3rM{KgM@j-r135JkkZI2R(1+U z2P?^hWZM+nUmb`~Zb6(&NIJ|NRhKysy`6W?gn~`ht3(J2{ozX7>|!I&fYZ(R0@W3Q z(V7iajgl(+v~t_Q !{I{yuz9m);jdxSNL32SNo|GI~ z$sr!=HV$nipHm3P`hJFmNVGkwY^cT@a@nGtR9H{s9Ym#Qjm`XB&f{H6#|vE66?j_G zUb$|2=Ki?y&Z4KRj4MUN_40Z}>Ttv~1RoJ+2Jxa$LQ%z`W_MHtbyJ2Y8J~H9qBF_Y zO_;x4ni-?(9gIIR7go?$4CVQlUgvk(!7dr$&}jbeOy_V1zG{|KF-yaI1k;lIl!Q`) zAQsX}o*cZob&r2rER3-KW;soMT3Ot&gC&SoJXeD}xLygl4?^dlKR!JT^JW>hVWlZ8 z%HvE=nRc*cX8(zyh!_l%Q8`uCkD`g4dmhC;B0E2ZP`P&9v;D{DUFL0+aHPeH7{YZ} z{3m4AOvFcF0>1j-gp;XR0nFc#1me-(t-0(F`i*VL^;s#Z&=t2Ft(MIB4Pj2EP%j`M zY8Bxhmj$SP4(05oMCvE F|4b5JlvK_hoW*tjRzSvJ>_*u2CWW9&=tz0l2EH2rSdUa} zI05HN-CUAnvRS3bnjZN5JIU^zkZFWUD%Zv_BaD7d6K^?64m(XUA= kp QgH-HbE39?ky9*XyW~SvI(wI;U`gkRLZX_mHtEoDGPLtltok^iiXn z1B;G3g3S(sL8tuLuFxkvaPw}~InIj?*GON5 yZ zJ)QZ7urdwCt3vrIiyl>M_C%tY=` zHjaCR;5DD`f}Mh=l>CXVWAW(R*uQL+eEPn`JB(9GISNt$hSa^-XQc>OO_KXG!$zDo znghRtM(_h$Yvl*R!xAeBl~DSiPrS)W5TWkO#)p}H4r&>c$31+wt7YN$7w;8m-JldO zb@;x1;T-0e#*!&`;fXC|A7VOlJ)kgE7(L4?qETTZ-CbZRrjyN{uOe4gLa?5|jJo~e zJe?rA={>lI6+t)Z&W3j|_X^ya(?Zj+4;@8>(#_YXhS+iE^3A8qyaD3LUEH-VqYVX< zZ(dVy7@NNFYVMs&F{ZyeSRrKO;*cmj&D0&R@3#4(>UZHAkK(%|)=i@KvSQAUoD@Rm zk+hBA0n}T@OE`i)vE&gw=u6;z9*+LDw=0QBnTYR&EmUm#o8Fd&n8yZl)zit6!6WDJ zMlTo|#8eAia62X&9YrI4W7$)#J@{UjmhWp2sJ?5dgc?rFhDv#G^p0wgCdB-fW!*WZ zgaj5&^slacL#imV?kAOPeNcZ+1~wdiXP}K$N!jlE!LM}-Lb1_krw1(?47FK&q$kv2 zbd!}W7SzQ*-pL`s)NRF7is#P>kRhm@4qnCFcMk-Ypii$d_@D1McT1YvR|QqJAickm zLU7-pv0t%6ZEo?poTdL_R#3M!`H}za3~~()-$kbr!>b|Kue?N}AE!9 O&G4pWr2{P$2Pkht$I;KT+1Cfmp~UQL!9V&hZmgX8TPRR6K)9)J{#HhZ7A03 z*C2O`@RLCbGC!`9Os(_#!^OADUa!R-KMVBIBm3fsg|wMP3*tw8;M(VOL2`_T;vA}p z;6d>l>%3;?scTcRS7L7dX5BM_&C(%<9W{RMOyxFE4B2i^GyAlBV1zhs!z4kdFx>|h z=2-X{Y*1fxl_+C*&II%>abM@}&hw(uqf-c*n<*1n67UI`<72*(p$g 4gx$ zMKIr$gd7+c*xmiUrR8;LtZBQO-}`<<$K%-VtNm~pLcGMe=baYm(F0n-1^Jp(_*k{1 zIfs!1wJK&Qe;lybTE>3sBSGf5YNo`of0Zo?p?T`(d9XT8{^>|cTT4W(G$pTFrd6Q{ zcSAQ6XZK4HVVGG2^wbD2NN5EO4rV-K_a4~(a;Jc9JQ< {Kvr(nK1b$48da9qA_6zbAu}jql)6Bg>Gw> zlEG^z?-W0Td^yOS$@KP+>uoRBPSn1A)0%b1D(BQelRnb~qfCkZ b{(6mrM?k?0)&MWiOuK`;S()~_@hiEkb*w=EhxP+(38 zrIgMLvA35rn))O~6Yq0JUZ4}dUO6)O9oVssl#HZ#plc1J=kRn$l5J&yDMM6$@*K1q zngYRv)hg|(cJ{YSm4OQ2_Tu*TrsMI7SxiGQdf&d!5xA4Op1u8;xn+k^@qPnbX9Ybx zCju{u|8an&LynTEKfPOJ;pOlf;W6HhEOQgP57nOtg-=%{#RQgZJN_YHB=071@i znGC8qvZoQgFrrER+l1k}Q4t;n2}rO=-UsI~wlhf me@D~P$%|u=9tELla`DV;sjz-y*B2iqv5+5`{ zp15DH$Z6nyTH7*I9;y6w4GjFlXq$so+M3^U9_=3riNkga!18?;c@**-0Fee{Lfpo` z x1-Vv1Zp)?#CDTVu zdXH=s;p!`gr&J?uqXRziT<~V6hR z;0Yykm3ol ;sDV%HwO`k} ycNQhy!sc@IH@_ zk!l- mh7~pA=@^ p$ OLu6UJ zaPBCK==6~(wx3kjG^0`|8^h0Uu35pZ3LPXUj%ICQ7Gy+;p2`udBEVzkJxXL#*>ngG zcKHM;l>|M&7RrxwrTDwwlFql?XR9&m#Homj=fBz%&(Ep59`w4w^hbu|^RA~P@h5~c z4_;dmMNexLDU*U?PYu!+AA!4!L!lm>(HM{&HqRA}ld|3$=Nh@X6-f?ei?MS02Kr2= zt*^yd$TYuv1o3!)uRRbKUgwCM)Su+d3V}+IwF2WlrtXLdVcj`$GJ0N^RkvuvC1#S( zP&mOByDeKIz4*P-(@K&mej5cE+u!P|itD>B%o=JuqBpK)@r~+2p8{a@a%6RH>n*=J z-(S=$BlSd=RJ5nId~Xix>gs|Zc7BJ_iZDMwz^6Q1Ah fl{#Fbu5Kq2V@K+c5OF2W&{mU~)SmMhNlxxJWtkm+$mWcm zR(UxkPkeRA-%FGlFdE{LDIz0-MDQ5owaUx$=;C-V*X1y)Jv^*)$ i=TPnMl@l#-s{#d;$oXD;Q*Fr7BuTe0eAIy9!#dBmg1`E+U zh$6M_hSdn&_;&NGU1&k3A918Otd>S<8w@eBkErsBVdK+QI-L0~MQR~RnbkL7lN;Er z%)x3Ex@m$58vP|{$5E!o_gL~Np9WOwsmKL9gp_bLwl6{NO0e>YImc(RH%;EYh K_=0k!P4#LE>k^=u#thj27+E&E9 k-YIk zbvA++Z3j?0TWxN#V}@4GadU0oI8oK*ZlX++H`)8BmJB&BzBF&-6GU%VWYqW-?%-qw z=h6pd6=j5Lq+!vbCkZs9hZi)sNjWE-?_U&I1FLYc-jA_Jxv)+#wkz7=oi)$L$GPFN z{oba#6S}~MVB*1jZwd$|b}>8A01;p=hSZqsVwDvUBB&infkJjT#g2FYQ9lPZ1`%eU zr)9z|T0##yf#2`YIH3J_3#0%)M3ip!S*hGkpLVEa4sY=+c=63va&NLI*))LG&9Z*M z!xJ9$n!zdCo~hNEe8D3t^)w^h(hF(61LF4J& c_2kEM= zX;j$);5p3GVSbqHyoG-V#j@zr_lTRrw=3+1aFYH& =;Pq4MW%|rBr z=R~Ms=^Kew$~M7pLWPWaM!wn{-(1#hh)L!3p|f5}V`Y=m`OFtb_uNna@4WqG>zrdc zLB5O4L>731Ztkt;*KDxuLnqHdu>l6B+DCXvoAGUh_9-a$(Edq(Ry{b0+9|>nggZ;W zE1j _8zH8R%uhiv4DmOGY!HZ!O8IQI>f1VM+6DHtJz43_TFZt!*9F zJ+>U!aTN=}!`=koM_rBIUqhX9FFxENt`2wV*i0CvFrAQUg5RCea|w`xtu*-gmy_K- zrh8m6)~no;9S@Op-z;8YvkfkvWk-PKjRMwclF?$Nr6R))9Js@j*|(sNZdJU!Dj~Xi zx(MdoG}G<8S`NK;8d5U~E%bKL7wcj2+bnAmGos_~80wsHiSDMeet;{Zk+Pdo3znhJ z=MNBacLek~Voj!gaDsIPV+5ALWEt9qb4`(2N;dSFX%}d47Qu_eBJ73%Wv18!TT4Rr zck=5|&B^&M!6m<)1iYk a_* z?|#XR(-QTF+lW^qKGMu`#i&~`2yJ2P@UpUYbj4#nbiu_kl<-p9qZ834^GjeFO$55K zWJQjH;w^H)f`kX!_A=Q@M{17(buRpR>|y?mrpL1O3hnJxG1_>aHOLI$g`!aVc+Db& zEQx){H*w*j6QLmoP0v^f zZo EJbJ^evIniqh1ir& zvBEnX$ *XA2eyLCO!I*B9qxF+x{{Smil1E*iXd^l~XePy?m$6J}Vl0?5f{xYs z2xql|t#R*@`%XQ^%n}olw>rev37?Ow- ybku&Efo+(+_YjFYDy!SR z3c5_C#u(%(%aI=fdS+}DNgdjz;3?L>s-xW>b>_@~UK%}S+Fx?HA=aK w7Oq$o!aD7Esko($_=DwYWXJr?G zH(EdJ$a(rzot$CC_&wzUZdghSmWA$nOX-`@uthYr`44ezwS7k(w>GTb_z+)iHIBf| z=>wNO^o*&)Z%hl#$-KDk=c4w<%}64;^~ZZVa?G3xZAU%O_{o^Md{$%FX%PIyYun7j zdbzgGQp!&sF!7SfadY;J4(sZh{?{wI8aBhjOi e*5r3?caVDHXrQIix9Z)8r7E%8 zpx!81-i1b$Nv9+U7-DHz0a-wEGBQNaGh^=y?Gl)@1)n%dZe&ziRT+^iMO<<%YhG!a zh(te@bY0O^mKWnm)w_tjCYdwJn0x;r=*p|XC*Zi9Z1D$nf88CG+ql~yJMXBvq1{q8 zCw<7;qC)*uB1U%|Y@EndbX#NzkdJqpb=T_wN} DFQa!gakdG|wfPFa%_P{lMriA36wwu=^z}-?@=EoWP)%ESmb`^++k>wK2 zrCUQcnoD&VDT}Nm!%GL(xOKOD4-9 LFo%j0VpOZ9g$n)35^mx9%xBf_1D3#ihtIWolGRXo{Idrx=t?_Zy57A#t|FN~$oB zG6XQ!Yh#ol2s_-HxN!G=(`L-ABQdb&pK%zzyxL@>{ZM_X+PcOqN!2&i?8cr)-*umt z_Q|=dP!KXY#sLBXHU-8P#@ #M_1D -#Y%}o%$<#`X@))lg z<`)E&TI3W?`ZGx0x{QQcys`XbKr9p0x2waWy`<3_{ZQmBi|w 5yn|t!VT|vuOD0U T-+0(DU(~cL2-5~hv{=*s%I7Uvw47r;?3oPF*efXu zL#~C$-a@rGgJhE#N4InqWO`;E@rOnQY}c`{kO8E9dBqw!QI}V3r>bV#I-zf+P$?n= zZN7wB-8n<&B@CPxl;`^#z}HM1`&_uZ+0YNkf>GR$5y~Vdy9MUYVh|TZEg Qv%I)(b>Pb&>;7{GC|eUuiLKw`qvVP&sSItv8~f?o155SuFKD zf|p-@L=P(PN>4dmVmKb4iCouP1ts5n;;p4!3QBc R{z@lIyq>uu?gAe zUQm>y8b;{5ZhY`5 -rA+1Ft zn#ytN&u42I<9!#&)3HW~d9_!Zu*`INn!_DCtE_3I=(E9ZUM{y(54Jykh`7K1yu30@ zACq=gEwlFmS%-*QbjK`w*ln59 {~t66 jTA_)J50RnN|s5jq;G zvnGe69i^ATjyBaJ5#j@_bO@436x&IYgFi|6(c<>SM+cy)h8<(1irh{1%BPHzKDtQt zZY$KXuGl2ub+LS-M$D+WL5#qgqcXuAsUG1usOfj4$bF1OyV`yu)ms)rUpQLgprNv< zUllJvVzFP==GYY3e-kCdA)(e-AJL`Qa!^m(I$S1_Q!P5XUm{g{n>X5Hsb0*pi!!nD zO&3{%svpBfE8s%VCxuQbEzc90EZ>Mvd=a^VN~14sgc5Q=5e%}L4wxh(Z~*H{_9`(P zmTYePvg$RAL3@6*p!if8ie@Xrz&j@C@%##dK5K0;CiYO}&_3F-+yL()SO#ux$&Mc~ zux^R;T1C}WSjRJ8v=9tVxwve(znDN$sB74Z3&?m=ye{OsMV9PCBj?+^cP!stLr9P^ zLiiG|!f8N1IJWKBcr?l+-DxdU sF- zA=iW{-u8G|^!TQV)YwHvGW*Pxs?V}_iW}{OOZ3-%GKd%tJPYI65bun+E=PP0gz>(* z sBSnuSWa70bM|lp@ z<`N1#S}<<#B>F3gxBMS{N`>v;?8p&pn)_k}UQ*ntR+qR#n?uv)P}TU-r ?R&)xMxD?rFhp+IRJR$bqU5m$QZqErPAiG&p^EWcckvO%dXeF{o=hW51cb zy(9~7N@JeW1)A6ayMw(<@|{k 2_Ow&-BN3!`G5IgLoqv`&(Qi zxI@ikNK@qWREo7}S&dF$BnQI&wV0~(0yQS%O(@lHjF^+3mRyR^$4{ECXeD;9c>`gy zUF0B=rewD4`M2|N7iUvdn-=L!DI%QfJxtBHXAXdeT^mL`;VGQ=LSg1L@#obBDjHzc z=%cUa9TR3Ql^tyekc_# *-FMq2A`0e@+N(;F;ST!zs5k@77CElx%yup2Ad ?M zI1!f|`h2a&(N9I+B|Hu~fq#D OOu6wIS|gVHo)%MUCPV_Pi-Aui+ca`##>P z%i@MtWT8Vlo{DIWJCw`;^?m1%4;ydO-N`tYPhcIhuDBAVqfQHymhQ3~+NcLb#hb&H z;u@+v4g%|=Sf}ZeC(o~i(dx)QeMj2GnGJs6;;`Ds=F!B^tM55F>haHd%pP E?$@6ymh3H?E=0S2HbK~k>jqmJbAL&9UYAnMWS zk9{I=#H0iCqxt);&YPSoJ%&e*B8llPU 4@Q1}qh^cXR_>0f zvHU|0t}wmp%#VS9tv)E}WeTXcm}_$Z5r6*5p;kKe#SJbNgb55J!lq<)_)Ipq*a`@p zp<4uv;jl4fnCoPQv85p7jT878UDI^^;qr%jM;5rfJ2lZ^5m&t>7uI678fb-E)DcP~ zB$m0AHx6Efqh9VEFX4CUk~J#=)baL#U37L3D$^(?AU`hld5G4SEiesox>KI8dad?v z^jT@Hua<(g!M*5L2v}u4BDI!jTkA<~`EC|}^?{}Y2cq?OA^{Ik44Dh>7&gYv&J^v0 z`b#vfH#5iWw)PrUhjBxSSq}63(NoO8tJS};t$^;)lg>uMGJE`3@4RoVu7PP*ZJ9-r z9- !Os6g8%FAY_)yXc_B~}(q5kfs# z%^YWxTjHNNvZR<(?ZW@m4P=*+@_~a)2?<_4M0u!wa2$BW`Lw?8iyqsV -A8f6w8ECgHF5Q6l^`1%Er7>~ds-Hj~9JqHwBl)l-jx&y= zEE;8>G3*BXK3Y<$ (lnv|#cdjj%y^3;;#L;u zhY;^@VcIacH)7O~0&b|%&TJdT+gopiwGJ{{GoY^!ZT3!Qi41&H6Bb<~;{$Eu3G?31 zY(YeJ>?LU0+`!L0e)5wHN33y}GP<-} $n6#HjB5n_;f?zYTs)<`h9=QGA`(^T 8 zHAKouqO~k=U9sCoP>LvXBa8 oU`x;yW3y|8B|?rN z?%cs-#C_ERG}%I9Qz>KbW2$MCP(R`Fi{dX|Db+`6i>x}$Z|12ttP2Ws`8fy~!a*27 zHBBb_K#My=Kp8ldFWq%X$-GMvl!*(}SwAiVvh+lfN#(>rSF0UP8<_%hBv87I=lX#v zy_dEo>I37zT|~Pn$Z}$jx}YQ4PG9LXmG~Lrr$2F VA&Iy<#YNPq?Ey-uZ4R-uSWM zaQ}6NMQh*Fhb$tL9bNng`&3Tdi+4VbbIfe31j(bw82d7fSh|JRpjd-Y$eZqrtNC%C zt8%}_gbBN1?&QlPzbrfnnTSX-?y`ttrf@DSm)OkJpco%ZNOT YHcG@dAFZsd$$OoFB%u#53$MCT(08K_Kc7P(!%xW5JA0+veW%p-;3ZzKOA9=; zS=Rt__Fd+;VNxz|#ooP;Ay*F%#Pov}L$YTt^gHCr8x)us$*@I@=;_M}Q-ijlWwkRY z7L{DT>1SPpcA_QPHARKz)62Ew^(%T6FYG~nhP8W><<+~EMZ$xdZg4b(v_l hB#)WGeX1RnyQt{H#K|8g9>E?l)qfS-hCIl6)Xt6aj z{ps`1a&r;ST0vlKu(pPRq>$P$ >`*&s!OcJo!wFd-8Q-1lFu>x@Nie#*}*b{b7ic|ksxB~ zRTG;Xi|%qOko92qeX~Tk0M{N0^g$1ben|>)8`eDA)R7-?f!~GX+%%BH>tVv@2FEzi z8qtoc%$#Lx0r$cwjK$Zqc|9sn5d w%q$%SHcF@!sI8S9# zJKSceF2IcV@-k&jFcov%s@E`CoDe$_v2f0<3QXb64%uS=cFV)+fLPSR*YO EExZygk6a5niMvfBytc|9%Eu8JqX)y!C5m`{?_+3Fk$q=6(BuzB zsGok<=;yfIgqX=sj7-Jf`;MQ2O09!^ ``Wl5JPaT?bV|46Al)qp(gR5MNJ=9$beF(T0| |IzpR z`}yqataY8ecAoophZsCRZ3dgn{VBjA_gTKK=}d^uZi3t@FG%{bQC7A%y&{ZDE)mL^ zM(p9`Z(v&&fxY@kR#fmFVWzF^=n780W~g(0!45WO8Z1vzAm(I5+^;otDUVcN#0X)2 zk6_v&Yib#gU*_~^9!p4{+7<|!j(Gl|V?>Ex)=%WVZMNAl((6^mJ%6#xjWpV$tQ}Dd zx$xf8ZKKW+sF##E7Bhm~e7mF_?=D`lzkrDw%2ock&2}_+`yO^Pe`SvIyiO-z$D;+Y z>ijFW(tVf4dui)WoqOyI8M`x^p@n0H3yA1Qb9_ &Z8-SZ7DG?wMrtDQ@$-F?q^#%`_`B`HHU1P&FEI?mrSW zoG|6t6N}R3T(iS#jGoW8;Fs4Xel?J2J r6bToPsBDDLY2JZ0Q?63CrUH2d;LC=G1eF+9X=( z!9?QicwI}Y3d}(&2cLtA9b9 ntSM*Iu)5@c%e|Av+~1;J=6dwBx0YN>mBlxM*s;AzT-^?y8gq;9Tl-8O^NcmS zgb(0A7-0MYCR1WCE+J!|(%0L?mK`;;Y8Au$u1^7SY;#HTA*<{cX2bcTcIu`tS@Ef_ z3)bABlDb2;64~+@#nrD!1lHyo Ywq!5t+lT00PPulOv65{aOAP#$!7 zoS-us@~FFX#CN=ZDseKC2?|xXEm5yrQYH3h%>Qya8>v8Gw@W=CapKP+b$ctR`yOgC zQ@#j4Yt|C89r|%KFYfu0%Wu@qbG4Sk)?mX|vF^2sF2Y7JOMcjNeRF_Y&WD+^NEbP9 zc}6`i>5KK#m4LDmi@}(D3XZh2Gx?->B3Pz3 5&KY@c`|>2Xy$T(@z1Kgr7r^Nl^wJW{ z)toT0a5z5LM7T;t%4cL6y&L^>zp+9tD6VL+&2Y@j@-tWHh5GX0im7E~uar3Zy>Km) z#1yBz%~9|%%cZFAv{B+yuPV0!Pa7{IUHOHOt;B4@dHTk&C1=Qy*Go!;$9h`pj%E!4 zy)shKc5|#CUdPh~G4U_<^~_*oueS@!>%lJmHq0PMvX2iHk%x#TDfX^rQh)eI`q7&8 zF4#~7&-p4Z#|FnK6R?Tn@6afrim&m#7XQ14`rJE3u`d;RyhQb@OZ!(Ky_)i6T7G>g zkUxZw*nvly1HIciM*Caj*L|-`jpdPt$$Q;yi?Ae5n<+71mrC}^?y!_%P=4m1pT`jZ z?As&4sK1+|>~*hQ#P%lHk*%s#sg_+9LAgIde4WDfV}tZ$+x+K8rrsH^k8lVhVTpHD z8@;!P*u7e8q$7
l%|IM4mW(vqkU&GVM$%) zAmN?Who7K6I35I!y}b9(A1KyA``%DiH|gy?*{bwT%g#4%$-L_BiS4NW3wZr+v%xO+ zhzCZK#=Lp7P4VmxC3%-+$Q&0*of*YLa(Jrc-b3<*_MJ6 {hgFTE{m3tp3nZA*pfx!+#oMFZ4!Wq~YvfK{!xyxk>N$ z_Uu&dTMY(7HQ`;hgR70`XK 7*c~cYjfdGS z>J+Ire-{mn0by!z>?Bi1gKtCm1%TMeSY0)A0$hF*N6~A;D!ia_u&soY_mS;of*$r= zq0xmsaqY$}TVi)R{r9bJ0omahADd}`(B)|Vb$xZM?mZ3eulfV6g|kQ3HLQ7!ROZu% zC(?jcr{u~rJy_GQP$0eFj^701(Mby|=DB~O%q=#*>fB3 x4!r=rr@LI&lyAU*=nOm9i|TKOASVyCs9vq$qjeoC|~hS zoYn=7^wDX2T}xgdufA+4+)`rPc=jBl379UsRseCjsx^DpeA2mDE4?_lQq2j8z!@p; z?nzsca(c`A_(WoIqs3vuJ0RAT1~tINr1}p1@{UsGlmCeCE+NVgdN9_oxrV0PM`y+` z?{QPmeaew*M@+O&>&NiV{O{^#oy(Sb`PHj>u|gUJI`%>Hva2ANOy29;;w5zC_z_IB ze#c^II)l4g7&80on(v5I_PVNmHpq@HefOes2m! 9cKqS*1xyllq|(zfNHh<09xGcf4W@<~uJI3gFY{+RS-+4`RJCVnXDO5;;u(R*?Z z!E_6C_Zrsoq2Qf>LiLRvC-N=!k3zCV1W^`k$jMZ&L?)(?TEFh5n}-n8t#tqChQm@J zowm;w^g*=v09{e5YcC*1#~(BURSvX^2hGJNuaX0ISW3x~#RDlHvk>`fdQEyfP|AJ;pZNh7h`D%-bMDWNJz>_1A)?+$_~%TFi@m|A1ezIY@~qv?jLpcR zX&&CcFD$;=2CIV`2Ww8Xiue`o9Foosoei>HaU&8Rirdmps;>fVsIP9`>l40cn3F1p z`12p7HQo9jRKwhq=`{beZki&=Ka9m(R*=oryt~DI>wTO!_3)|P0NK?W(|n0;JGce9 zypND$&(ag;c EoYEdXa zU>yh{y&A8;`k gQ-QG6VvwjNcuLzJtYJ(9K1hcm#eM{5tPqo z|71C74>?(_k0gDnfmNw^YyN6GW0idIzoxw3yAAr~HSrbJjpPfFP6Uy56YKFX>l?gT zn?=Z2z!JQlX%^9BNNYcl0N-rJ^7=Tczit|jz0EP>siG6RCa~Yf9qUngL}RdU^&LyM fLukWe+?=|blEGq+6Y|#ym zt3wwuFO908Z>o2M&f|^`s(5^0a_IdQjEbL^ZF`-|QV*u=5~aT$Gz!e7e5ebxGIGQs ztrwMpp%pj2TW~+I1gtBq1r|NiikYO%j`m7qa__q9zX?%dLl25geEc?cw6lHFg?p~< ziHl<+30XN+Vd8Th&w`rLkJ3z+(}aD)3GIN{-W;VyDWq;mM3GWyoGn=mR2(bMFfP(C zgf##=S{%lZsXswMo?K=fzF6`BV44K{q}kqLGTGlW32Zzll2 3Oiv`p*hWHk@7T2Ifp&GUfXS$ z(8r%qKPc!U=}XOEC^MTZ#DgRi$){keC(Wt8y9T|hN&}!X*TW#D0h|Zv ^ zp-8n_00;Jfj_3@%SCl#C>`|bo6@R8?!ay5rw@X^9K5BST7H3Ltkl;|@r_fIc`CBKy z@N}(Zm=;CZ=@OQ_uydu`?G&zrk9LH1rOc;+86nGLOj$^6vjo*IO2buNk#=K25;p6T zvdJKq%#C9zRV8Bb)~6NbdU$78F(jy8`t6x)hn-Y+=(M`iQKTL}1Nw^P=G^}#k-Y1X z;2MF~$s7DaBAO67xtyqjtRu+05HAx;RmxAs-s)q>0?g0mu2-a ?j>UKR?S$}(80A_>aM=J~^)(kCCH;)&|KaGzc z%{2HFUslhC%Y_Rqka08Nu--k3Iy|Nd=K(>ZDM(f7c%uYs$CT$76<*}~99Lt!YSEfW z*g29?k0i3|271uv+Q9xXYU$x0S8w==?T;74;)p2ox)+LDO`Hy*s-KZ_M(Ed*K#iV% zLFjYim6b2(+c=i=pX%qWn{>h}p7~=1DE%if2zHo#O42t{8qTkUxdzdYmCGpEljqlc zMUfLnkrlN^nW$xj{a_y#2T~nvmZ8osC8|j(I9R&C81jD?X0I#}3^u9RJRs_IPWg-A z!`bCv=KJ54V+i5+K!rYWJ_CKEP}~odTJ2s-Lq0G3YF3PTw9|S^Jwq$cuSy~>09QDc znuNHrV6{@X@<-Ay #YQ3tv_u;DH4Jw5BFbV|u+P^5dM>uoppqKV|&P1lJ1%P+- s71^Dyd7$8mU^AWD!EXll40kRv| zA5q}ATV4asujw~Xd@BLSe|znxKGrUDZ?iK1rjcK#Uh P=Bp zi4PP|(5GAU67mjvyjo5fd%-GW@*)oJT7LP9V^|<@^DkW y_`D;~eZ!+p@^|%>^6RP74kPGTO)q0{ sv#(sZYWW|5=wrFgyU{?`seAq4)z-Q17 Pyir6nGL|Da+3jesf_p(nKSk0DoAJQAX7B@(X(%{!6jeHZ`BZ={ zPwgs}l>Zv71%eB47U{=+Ns8?qN9??!DIpNA1k;UWgJK%SCiik@>fKu>Qg@$>6 !;GkVoAhcFj3olsmID;$|NcnY^B^y!hogs0)UI4rLm%`7OAkN#>NW9Ewt~it z${@%?R89pAj-mNH>|IB3y#N`#T7gygDRWJdELrUJz2e qN7NtWx5{G_6!n${tQBO_l)& zsxZ<(5 t1tXQls*z>u>Z zxW>3fA7W4(IMmEj-4sy+V?btOF=$!DRVeN9DMez72|SjvhbR=J3F{Zu)7bPWTT3Xc zF$lEZ1WARSeh;DPS9A}WJttKkXnHeO{(qhkCVe3W7PBk2PsZo=vBZRr)jNmDw*|F^ z8H@N g$>5_`?bu@ zNO&jo8&+-7F=l7p;wEYy!0)d_djM{UG@zZJNnx6x|24v3r#E}Hg!h5!8=21%h#d85 zS2>?=3kj;7kLZ2(*J= KMc7OD-# zi~Uxq(fPJBz$<93#rAKFz6Rvk-%YnaNk=OSN$ce36uW)=-1%f&aHo|~y<(N3YRo9t zI3zNdmZI^{bdKcjit#miTobDJcV$I^X(lS(*nd0V|F=+i4fxmERc;p*5kK-D)8;A? f=ydb=^htl~8*yGno*a~W%oA0`*9sMK=FtBEnojlH literal 0 HcmV?d00001 diff --git a/package.json b/package.json index a8462a6d5dc3..b9ad10f47042 100755 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "antwar-helpers": "^0.19.0", "antwar-interactive": "^0.19.0", "async": "^2.5.0", - "autoprefixer": "^7.1.3", + "autoprefixer": "^7.2.3", "babel-core": "^6.26.0", "babel-eslint": "^7.2.3", "babel-loader": "^7.1.2", @@ -55,12 +55,12 @@ "babel-plugin-transform-object-rest-spread": "^6.26.0", "babel-preset-env": "^1.6.0", "babel-preset-react": "^6.24.1", - "copy-webpack-plugin": "^4.0.1", + "copy-webpack-plugin": "^4.3.0", "css-loader": "^0.28.5", "duplexer": "^0.1.1", "eslint": "4.5.0", "eslint-loader": "^1.9.0", - "eslint-plugin-markdown": "^1.0.0-beta.6", + "eslint-plugin-markdown": "^1.0.0-beta.7", "extract-text-webpack-plugin": "^3.0.0", "file-loader": "^0.11.2", "fontgen-loader": "^0.2.1", @@ -69,22 +69,22 @@ "github": "^10.0.0", "html-webpack-plugin": "^2.30.1", "http-server": "^0.10.0", - "hyperlink": "^3.0.0", + "hyperlink": "^3.0.1", "loader-utils": "^1.1.0", "lodash": "^4.17.4", "markdown-loader": "^2.0.1", "markdownlint": "^0.6.0", "markdownlint-cli": "^0.3.1", - "marked": "^0.3.6", + "marked": "^0.3.7", "mkdirp": "^0.5.1", "modularscale-sass": "^3.0.3", - "moment": "^2.18.1", + "moment": "^2.20.1", "ncp": "^2.0.0", "node-sass": "^4.5.3", "npm-run-all": "^4.1.1", "postcss-loader": "^2.0.6", "prism-languages": "^0.3.3", - "prismjs": "^1.6.0", + "prismjs": "^1.9.0", "raw-loader": "^0.5.1", "request": "^2.81.0", "sass-loader": "^6.0.6", @@ -94,14 +94,14 @@ "tap-parser": "^6.0.1", "through2": "^2.0.3", "url-loader": "^0.5.9", - "webpack": "^3.5.5", - "webpack-dev-server": "^2.7.1", + "webpack": "^3.10.0", + "webpack-dev-server": "^2.9.7", "webpack-merge": "^4.1.0", "yaml-frontmatter-loader": "^0.1.0" }, "dependencies": { - "ajv": "^5.2.2", - "preact": "^8.2.5", + "ajv": "^5.5.2", + "preact": "^8.2.7", "preact-compat": "3.17.0", "prop-types": "^15.5.10", "react": "^15.6.1", @@ -109,6 +109,7 @@ "react-router": "^4.2.0", "react-router-dom": "^4.2.2", "tool-list": "^0.11.0", + "webpack.vote": "^0.1.2", "whatwg-fetch": "^2.0.3" } } diff --git a/src/assets/homepage-modules.svg b/src/assets/homepage-modules.svg deleted file mode 100755 index a39441644b53..000000000000 --- a/src/assets/homepage-modules.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/components/Navigation/Links.json b/src/components/Navigation/Links.json index c46d5a0080c5..d7ec12ee950b 100644 --- a/src/components/Navigation/Links.json +++ b/src/components/Navigation/Links.json @@ -15,6 +15,10 @@ "title": "参与贡献", "url": "contribute" }, + { + "title": "投票", + "url": "vote" + }, { "title": "博客", "url": "//medium.com/webpack" diff --git a/src/components/NotificationBar/NotificationBar.jsx b/src/components/NotificationBar/NotificationBar.jsx index 41deb5c66aed..50a2faee4e9d 100755 --- a/src/components/NotificationBar/NotificationBar.jsx +++ b/src/components/NotificationBar/NotificationBar.jsx @@ -2,7 +2,7 @@ import React from 'react'; import Container from '../Container/Container'; import testLocalStorage from '../../utilities/test-local-storage'; -const version = '1'; +const version = '2'; const localStorageIsEnabled = testLocalStorage() !== false; export default class NotificationBar extends React.Component { @@ -13,17 +13,16 @@ export default class NotificationBar extends React.Component { ); diff --git a/src/components/NotificationBar/NotificationBar.scss b/src/components/NotificationBar/NotificationBar.scss index 4a5e0f308be5..4e8a3f0af00f 100755 --- a/src/components/NotificationBar/NotificationBar.scss +++ b/src/components/NotificationBar/NotificationBar.scss @@ -5,10 +5,6 @@ color: getColor(white); background: getColor(emperor); - p:not(:last-child) { - padding-bottom: 1em; - } - p { font-size: 14px; } @@ -36,15 +32,15 @@ } .notification-bar__close { - color: getColor(white); - font-size: 16px; - padding: 0; - background: none; - border: none; position: absolute; + font-size: 16px; top: 10px; right: 1em; + padding: 0; + border: none; cursor: pointer; + color: getColor(white); + background: none; transition: color 250ms; &:hover { diff --git a/src/components/Site/Site.jsx b/src/components/Site/Site.jsx index 7f09856e186b..fa98e96caa1b 100755 --- a/src/components/Site/Site.jsx +++ b/src/components/Site/Site.jsx @@ -45,9 +45,17 @@ const Site = ({ title: section.path.title, url: section.url, pages: section.pages.map(page => ({ + file: page.file, title: page.file.title, url: page.url - })) + })).sort(({ file: { attributes: a }}, { file: { attributes: b }}) => { + let group1 = a.group.toLowerCase(); + let group2 = b.group.toLowerCase(); + + if (group1 < group2) return -1; + if (group1 > group2) return 1; + return a.sort - b.sort; + }) })) } /> diff --git a/src/components/SplashViz/SplashViz.jsx b/src/components/SplashViz/SplashViz.jsx index 3cae30b3eba3..1c8f9f3e5ff3 100755 --- a/src/components/SplashViz/SplashViz.jsx +++ b/src/components/SplashViz/SplashViz.jsx @@ -2,7 +2,7 @@ import React from 'react'; import Cube from '../Cube/Cube'; import TextRotator from '../TextRotater/TextRotater'; -import Modules from '../../assets/homepage-modules.svg'; +import homeSVG from './SplashVizSVG'; export default class SplashViz extends React.Component { @@ -18,9 +18,7 @@ export default class SplashViz extends React.Component { styles -- 赞助 webpack,同时获取官方衣服!访问 webpack 官方商店! 查看所有收益请转到 webpack 的 Open Collective 页面! -
-- 在 Unixstickers! 上购买全新的 webpack 贴纸 - {localStorageIsEnabled ? - : - null - } + 赞助 webpack,同时从官方商店购买衣服{' '} + 或者在这里购买贴纸!所有收益将转到我们的{' '} + open collective!
+ { localStorageIsEnabled ? + : + null + }- -+); diff --git a/src/components/SplashViz/SplashViz.scss b/src/components/SplashViz/SplashViz.scss index 09b105f33a7f..2bd9894fa8b2 100755 --- a/src/components/SplashViz/SplashViz.scss +++ b/src/components/SplashViz/SplashViz.scss @@ -27,16 +27,17 @@ position: absolute; left: 0; right: 0; - top: 0; + top: 50%; // vertical center bottom: 0; width: 75vw; min-width: 550px; max-width: map-get($screens, large); - margin: auto; + margin: 0 auto; + transform: translateY(-50%); // vertical center display: none; @include break { - display: block; + display: table; // Get height of its child svg } img { diff --git a/src/components/SplashViz/SplashVizSVG.js b/src/components/SplashViz/SplashVizSVG.js new file mode 100644 index 000000000000..95e6ba1ad7ea --- /dev/null +++ b/src/components/SplashViz/SplashVizSVG.js @@ -0,0 +1,200 @@ +// TODO: Try raw-loader or other approach when build process is refactored +module.exports = { + body: ` + ` +}; diff --git a/src/components/StarterKits/StarterKits.jsx b/src/components/StarterKits/StarterKits.jsx index ac73d7efeb71..7d0a856cd5fa 100755 --- a/src/components/StarterKits/StarterKits.jsx +++ b/src/components/StarterKits/StarterKits.jsx @@ -17,15 +17,15 @@ const StarterKits = props => ( 下面表格包含一个起步配套工具的辅助列表, 可以作为基于 webpack 的项目的起点。 - 要向列表添加新的配套工具请访问 - 仓库 + 要向列表添加新的配套工具请访问{' '} + 仓库{' '} 以及提交一个 PR 来修改这个文件
generator/starterProjectUrls.js
.- 模板应该是用于验证概念, + 模板应该是用于验证概念, 以帮助你学习各种框架的 webpack 不同技术。 确保你了解他们在做什么,并避免复制和粘贴制造重复代码。 另外还请注意,这些都不是由 webpack 官方支持的。 diff --git a/src/components/Vote/App.jsx b/src/components/Vote/App.jsx deleted file mode 100755 index cfba8704f200..000000000000 --- a/src/components/Vote/App.jsx +++ /dev/null @@ -1,411 +0,0 @@ -import React from 'react'; -import 'whatwg-fetch'; -import * as api from "./api"; -import VoteButton from './Button/Button'; -import Influence from './Influence'; -import GithubMark from '../../assets/github-logo.svg'; - -function updateByProperty(array, property, propertyValue, update) { - return array.map(item => { - if(item[property] === propertyValue) { - return update(item); - } else { - return item; - } - }); -} - -export default class VoteApp extends React.Component { - constructor(props) { - super(props); - this.state = { - selfInfo: undefined, - listInfo: undefined, - isFetchingSelf: false, - isVoting: 0 - }; - } - - isBrowserSupported() { - return typeof localStorage === 'object'; - } - - componentDidMount() { - if(!this.isBrowserSupported()) - return; - - let { selfInfo, listInfo } = this.state; - - if(api.isLoginActive()) { - this.setState({ - isLoginActive: true - }); - api.continueLogin().then(token => { - window.localStorage.voteAppToken = token; - }); - } else { - if(!selfInfo) { - this.updateSelf(); - } - if(!listInfo) { - this.updateList(); - } - } - } - - componentWillReceiveProps(props) { - if(!this.isBrowserSupported()) - return; - - this.updateList(props); - } - - updateSelf() { - let { voteAppToken } = localStorage; - if(voteAppToken) { - this.setState({ - isFetchingSelf: true - }); - api.getSelf(voteAppToken).catch(e => { - this.setState({ - selfInfo: null, - isFetchingSelf: false - }); - }).then(result => { - this.setState({ - selfInfo: result, - isFetchingSelf: false - }); - }); - } - } - - updateList(props = this.props) { - let { name } = props; - let { voteAppToken } = localStorage; - this.setState({ - isFetchingList: true - }); - api.getList(voteAppToken, name).catch(e => { - this.setState({ - listInfo: null, - isFetchingList: false - }); - }).then(result => { - this.setState({ - listInfo: result, - isFetchingList: false - }); - }); - } - - localVote(itemId, voteName, diffValue, currencyName, score) { - let { selfInfo, listInfo } = this.state; - this.setState({ - isVoting: this.state.isVoting + 1, - listInfo: listInfo && { - ...listInfo, - items: updateByProperty(listInfo.items, "id", itemId, item => ({ - ...item, - votes: updateByProperty(item.votes, "name", voteName, vote => ({ - ...vote, - votes: vote.votes + diffValue - })), - userVotes: updateByProperty(item.userVotes, "name", voteName, vote => ({ - ...vote, - votes: vote.votes + diffValue - })), - score: item.score + score * diffValue - })) - }, - selfInfo: selfInfo && { - ...selfInfo, - currencies: updateByProperty(selfInfo.currencies, "name", currencyName, currency => ({ - ...currency, - used: currency.used + diffValue, - remaining: currency.remaining - diffValue - })) - } - }); - } - - vote(itemId, voteName, diffValue, currencyName, score) { - if(!diffValue) return; - this.localVote(itemId, voteName, diffValue, currencyName, score); - let { voteAppToken } = localStorage; - api.vote(voteAppToken, itemId, voteName, diffValue).catch(e => { - console.error(e); - // revert local vote - this.localVote(itemId, voteName, -diffValue, currencyName, score); - this.setState({ - isVoting: this.state.isVoting - 1 - }); - }).then(() => { - this.setState({ - isVoting: this.state.isVoting - 1 - }); - }); - } - - render() { - let { name } = this.props; - - if(!this.isBrowserSupported()) - returnYour browser is not supported.; - - let { selfInfo, listInfo, isVoting, isFetchingList, isFetchingSelf, isCreating, isLoginActive, editItem, editItemTitle, editItemDescription } = this.state; - - let { voteAppToken } = localStorage; - - if(isLoginActive) { - returnLogging in...; - } - - const inProgress = isFetchingList || isFetchingSelf || isCreating || isVoting; - - let maxVoteInfo = listInfo && listInfo.possibleVotes.map(() => 0); - - if(listInfo) listInfo.items.forEach(item => { - if(item.userVotes) { - maxVoteInfo.forEach((max, idx) => { - let votes = item.userVotes[idx].votes; - if(votes > max) - maxVoteInfo[idx] = votes; - }); - } - }); - return ( --- ); - } - - renderSelf (inProgress) { - let { listInfo, selfInfo, isFetchingSelf } = this.state; - if(!selfInfo) { - if(isFetchingSelf) { - return- Vote ---- { listInfo &&------- - - DISCLAIMER: Since this feature is its Alpha stages, the formula for calculating influence may change. --- {this.renderSelf(inProgress)} ---} -{listInfo.displayName}
-{listInfo.description}-- { listInfo.items.map(item =>
-- -
)} - { listInfo.isAdmin &&---- { editItem !== item.id &&{item.score}- {listInfo.possibleVotes.map((voteSettings, idx) => { - let vote = item.votes[idx]; - let userVote = item.userVotes && item.userVotes[idx]; - let currencyInfo = selfInfo && voteSettings.currency && this.findByName(selfInfo.currencies, voteSettings.currency); - let maximum = voteSettings.maximum || 1000; // infinity - let minimum = voteSettings.minimum || 0; - let value = (userVote && userVote.votes) ? userVote.votes: 0; - if(currencyInfo && currencyInfo.remaining + value < maximum) maximum = currencyInfo.remaining + value; - return-; - })} -{ - this.vote(item.id, voteSettings.name, diffValue, voteSettings.currency, voteSettings.score); - }} - /> - - {item.title} - {item.description} - { listInfo.isAdmin &&} - { editItem === item.id &&- - - - - -} --} -- this.setState({ editItemTitle: e.target.value })} /> ---- -- -
} -this.setState({newTitle: e.target.value})} />- - -Loading user info...; - } - return ; - } else { - return-; - } - } - - findByName(array, name) { - for(var i = 0; i < array.length; i++) - if(array[i].name === name) - return array[i]; - return null; - } - - getNiceVoteValues(maximum) { - var arr = []; - var b = true; - for(var x = 1; x < maximum; x *= b ? 5 : 2, b = !b) { - arr.push(x); - } - if(maximum) - arr.push(maximum); - return arr; - } - - getStep(maximum) { - return Math.floor(maximum / 20) * 2 || 1; - } - - getColor(name) { - switch(name) { - case "influence": return "blue"; - case "golden": return "#bfa203"; - case "thumb": return "#535353"; - } - return undefined; - } -} diff --git a/src/components/Vote/App.scss b/src/components/Vote/App.scss deleted file mode 100755 index bc91ef1c6be1..000000000000 --- a/src/components/Vote/App.scss +++ /dev/null @@ -1,228 +0,0 @@ -@import 'vars'; -@import 'mixins'; -@import 'functions'; - -.vote-app { - margin: 1.5em; - - &__top { - display: flex; - flex-direction: column; - - @include break(medium) { - flex-direction: row; - } - } - - &__influence { - flex: 0 0 75%; - } - - &__influence-description { - display: flex; - flex-direction: column; - - @include break(medium) { - flex-direction: row; - } - } - - &__user-section { - padding: 0 0 30px; - - @include break(medium) { - flex: 0 0 25%; - padding: 0 0 0 20px; - } - - @include break(large) { - border-left: 2px solid getColor(elephant); - } - } - - &__influence-section { - flex: 0 0 50%; - } - - &__influence-disclaimer { - padding: 1em 0; - font-size: smaller; - color: red; - - @include break(medium) { - text-align: center; - } - } - - &__login-button { - margin: 20px 0; - - button { - border: none; - outline: none; - color: getColor(white); - background: getColor(elephant); - padding: 5px 10px 5px 10px; - border-radius: 2px; - font-size: 13px; - cursor: pointer; - - &:hover { - background: black; - } - - &:active { - background: getColor(elephant) - } - } - - img { - height: 25px; - vertical-align: middle; - padding-left: 5px; - } - } - - &__userinfo { - outline: none; - border: none; - background: getColor(elephant); - color: white; - border-radius: 3px; - font-size: 13px; - display: inline-block; - padding: 3px 10px; - - img { - height: 25px; - vertical-align: middle; - margin-right: 10px; - } - } - - &__self-info { - margin-top: 10px; - } - - &__button-area { - margin: 5px 0; - } - - &__update-button { - - } - - h1 { - font-size: 16pt; - font-weight: bold; - margin-bottom: 10px; - } - - &__item-title { - font-size: 16pt; - font-weight: bold; - margin-right: 10px; - } - - &__item-edit-title { - width: 100%; - } - - &__item-edit-description { - width: 100%; - } - - &__currency-list { - display: block; - - & > li { - display: inline-block; - padding: 5px; - } - } - - &__item-card { - display: flex; - flex-direction: column; - margin-top: 30px; - margin-bottom: 30px; - - @include break(medium){ - flex-direction: row; - } - } - - &__score-section { - display: flex; - border: 1px solid lightgray; - user-select: none; - flex-wrap: wrap; - - @include break(medium){ - flex: 0 0 40%; - margin-right: 30px; - padding: 0 0 0 20px; - } - } - - &__item-score { - align-self: center; - font-size: 20pt; - flex: 0 0 100%; - text-align: center; - - @include break(medium){ - flex: 0 0 20%; - text-align: left; - } - } - - &__item-button { - align-self: center; - flex: 0 0 50%; - - @include break(medium){ - flex: 0 0 40%; - } - } - - &__items-list { - display: block; - - & > li { - display: block; - padding: 5px 0; - } - } - - &__currency-influence, &__vote-influence { - color: blue; - &:before { - content: "◆\2009"; - } - } - - &__currency-goldenInfluence, &__vote-golden { - color: #bfa203; - &:before { - content: "◇\2009"; - } - } - - &__currency-support, &__vote-support { - color: green; - &:before { - content: "$\2009"; - } - } - - &__vote-thumb { - - } - - &__admin { - input, textarea, button { - width: 100%; - } - } -} diff --git a/src/components/Vote/Button/Button.jsx b/src/components/Vote/Button/Button.jsx deleted file mode 100755 index 0cff5ba33ec1..000000000000 --- a/src/components/Vote/Button/Button.jsx +++ /dev/null @@ -1,132 +0,0 @@ -import React, {Component} from 'react'; - -export default class NewButton extends Component { - handleClick (n) { - const {maxUp, maxDown, onVote} = this.props; - onVote(Math.min(maxUp, Math.max(n, -maxDown))); - return false; - } - - titleText (n, maxUp, maxDown) { - n = Math.min(maxUp, Math.max(n, -maxDown)); - if(n === 0) - return ""; - return n > 0 ? "+" + n : "" + n; - } - - makeTriangle (n, fn, size, minForEnabled, increase) { - const {maxUp, maxDown, color} = this.props; - const enabled = n !== 0 && (n > 0 ? (maxUp >= minForEnabled) : (maxDown >= minForEnabled)); - const className = "vote-new-button__upDown"; - - if(enabled) { - return this.handleClick(n)} - onMouseDown={() => this.startCounter(increase)} - onMouseUp={() => this.stopCounter()} - onMouseOut={() => this.stopCounter()} - onTouchStart={() => this.startCounter(increase)} - onTouchEnd={() => this.stopCounter()} - onTouchCancel={() => this.stopCounter()} - className={className} - > - {fn({size: size, color: color})} - ; - } else { - return - {fn({size: size, color: "#eee"})} - ; - } - } - - startCounter(increase) { - let current = 0; - let add = 0; - const that = this; - - if (this.interval) { - clearInterval(this.interval); - } - - this.interval = setInterval(function() { - // increase for 1 between 0 and 5 - if(current <= 5) { - current++; - add = 1; - } - // increase for 2 between 6 and 10 - else if(current <= 10) { - current+=2; - add = 2; - } - // increase for 5 between 11 and 40 - else if(current <= 40) { - current+=5; - add = 5; - } - // increase for 10 between 41 and 70 - else if(current <= 70) { - current+=10; - add = 10; - } - // increase for 15 after 71 - else { - current+=15; - add = 15; - } - - if(!increase) { - add *= -1; - } - - that.handleClick(add); - }, 200); - } - - stopCounter() { - if (this.interval) { - clearInterval(this.interval); - } - } - - render() { - const {color, className, value, myValue, canVote} = this.props; - return canVote ? (- - {selfInfo.login} -- { listInfo &&- { selfInfo.currencies - .filter(currency => listInfo.possibleVotes.some(voteSettings => voteSettings.currency === currency.name)) - .map(currency =>
} -- - {currency.remaining} {currency.displayName} -
) } -- - ---): (- {this.makeTriangle(1, triangleUp, 10, 1, true)} - {this.makeTriangle(-1, triangleDown, 10, 1, false)} --- {value} --- ({myValue}) ---); - } -} - -function triangleUp({color, size}) { - let path = `m ${size},0 -${size},${size / 3 * 2} ${size*2},0 z`; - return ; -} - -function triangleDown({color, size}) { - let path = `m ${size},${size / 3 * 2} ${size},-${size / 3 * 2} -${size*2},0 z`; - return ; -} \ No newline at end of file diff --git a/src/components/Vote/Button/Button.scss b/src/components/Vote/Button/Button.scss deleted file mode 100755 index 7a3419a7c8a1..000000000000 --- a/src/components/Vote/Button/Button.scss +++ /dev/null @@ -1,57 +0,0 @@ -@import 'vars'; -@import 'mixins'; -@import 'functions'; - -.vote-button { - text-align: center; - &__value { - font-size: 150%; - } - - &__value, &__my-value, &__upDown { - display: block; - width: 90px; - } - - &__upDown { - border: 0; - padding: 0; - margin: 0; - } -} - -.vote-new-button { - display: flex; - justify-content: center; - - @include break(medium) { - justify-content: inherit; - } - - &__arrows { - flex: 0 0 10%; - } - - &__value { - font-size: 15pt; - } - - &__value, &__my-value{ - display: inline; - margin-left: 5px; - align-self: center; - } - - &__upDown { - border: 0; - padding: 0; - margin: 0; - cursor: pointer; - } - - &__logout-value { - align-self: center; - font-size: 20pt; - margin: auto; - } -} \ No newline at end of file diff --git a/src/components/Vote/Influence.jsx b/src/components/Vote/Influence.jsx deleted file mode 100755 index e2ada69ddfca..000000000000 --- a/src/components/Vote/Influence.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; - -export default class InfluenceComponent extends React.Component { - constructor(props) { - super(props); - } - - render() { - return (this.props.type === "normal" ? ( -- {value} --- - ) : ( -Influence
-Influence is a unit of measure based on time you have been a member on GitHub. However, in 2017 and on you will recieve one influence per day.
-- - )); - } -} diff --git a/src/components/Vote/Influence.scss b/src/components/Vote/Influence.scss deleted file mode 100755 index 7e9eba35bc84..000000000000 --- a/src/components/Vote/Influence.scss +++ /dev/null @@ -1,19 +0,0 @@ -@import 'mixins'; -@import 'functions'; - -.influence-info { - em { - font-weight: bolder; - } - - i { - font-style: italic; - } - - &__section { - padding: 0.5em 0; - @include break(medium) { - padding: 0 .5em; - } - } -} \ No newline at end of file diff --git a/src/components/Vote/Vote.jsx b/src/components/Vote/Vote.jsx index c913a5c50363..afd37f9ac1a5 100755 --- a/src/components/Vote/Vote.jsx +++ b/src/components/Vote/Vote.jsx @@ -1,30 +1,19 @@ import React from 'react'; import Interactive from 'antwar-interactive'; import Container from '../Container/Container'; -import VoteApp from './App'; -import '../../styles'; +import VoteApp from 'webpack.vote'; +import 'webpack.vote/dist/style.min.css'; import './Vote.scss'; -import './App.scss'; -import './Influence.scss'; -import './Button/Button.scss'; -const Vote = ({ section, page }) => { - let arr = page.url.split('/').filter(Boolean); - let name = arr[arr.length - 1]; - - return ( -Golden Influence
-Golden Influence is equal to 100 normal influence. Golden Influence is obtained by being a backer or sponsor on our Open Collective page.
-- - - ); -}; +const Vote = ({ section, page }) => ( +- -- + +); Vote.title = 'Vote'; diff --git a/src/components/Vote/Vote.scss b/src/components/Vote/Vote.scss index 8aa896a548e1..d9efe95ce407 100755 --- a/src/components/Vote/Vote.scss +++ b/src/components/Vote/Vote.scss @@ -2,3 +2,10 @@ @import 'mixins'; @import 'functions'; +.vote { + h1, h2, h3, h4, h5, h6 { + font-family: $font-stack-heading; + font-weight: 600; + color: getColor(fiord); + } +} diff --git a/src/components/Vote/api.dev.js b/src/components/Vote/api.dev.js deleted file mode 100755 index 8aac367b8abc..000000000000 --- a/src/components/Vote/api.dev.js +++ /dev/null @@ -1,161 +0,0 @@ -let usedCurrencies = { - influence: 100, - goldenInfluence: 100 -}; -let totalCurrencies = { - influence: 1000, - goldenInfluence: 300 -}; -let lists = { - todo: { - possibleVotes: [ - { - name: "influence", - currency: "influence", - score: 1, - color: "blue" - }, - { - name: "golden", - currency: "goldenInfluence", - score: 1, - color: "#bfa203" - } - ], - items: [ - { id: "1234", list: "todo", title: "Finish up MVP documentation", description: "Take care for the remaining issues in the webpack.js.org repo which are relevant for the MVP.", influence: 15 }, - { id: "2345", list: "todo", title: "Review whole documentation", description: "Read over **all** of the documentation to find errors.", golden: 20 }, - ] - } -}; -let allItems = { - "1234": lists.todo.items[0], - "2345": lists.todo.items[1], -}; - -function delay(time) { - return new Promise(function (fulfill) { - setTimeout(fulfill, time); - }); -} - -function clone(json) { - return JSON.parse(JSON.stringify(json)); -} - -export function isLoginActive() { - return /^\?login=/.test(window.location.search); -} - -export function startLogin(callbackUrl) { - window.location.search = "?login=" + encodeURIComponent(callbackUrl); - return Promise.resolve(); -} - -export function continueLogin() { - if(/^\?login=/.test(window.location.search)) { - return delay(2000).then(() => { - setTimeout(() => window.location = decodeURIComponent(window.location.search.substr(7), 100)); - return "developer"; - }); - } - return Promise.resolve(); -} - -export function getSelf(token) { - if(token !== "developer") - return Promise.reject(new Error("Not logged in as developer")); - return delay(500).then(() => ({ - login: "dev", - name: "Developer", - avatar: "https://github.com/webpack.png", - currencies: [ - { name: "influence", displayName: "Influence", description: "Some **description**", value: totalCurrencies.influence, used: usedCurrencies.influence, remaining: totalCurrencies.influence - usedCurrencies.influence }, - { name: "goldenInfluence", displayName: "Golden Influence", description: "Some **description**", value: totalCurrencies.goldenInfluence, used: usedCurrencies.goldenInfluence, remaining: totalCurrencies.goldenInfluence - usedCurrencies.goldenInfluence } - ] - })); -} - -export function getList(token, name) { - const loggedIn = token === "developer"; - const listData = lists[name]; - return delay(500).then(() => ({ - name: name, - displayName: "DEV: " + name, - description: "Some **description**", - lockable: true, - deletable: true, - archivable: true, - isAdmin: true, - possibleVotes: listData.possibleVotes, - items: lists[name].items.map(item => { - const votes = listData.possibleVotes.map(pv => ({ - name: pv.name, - votes: (item[pv.name] || 0) + Math.floor(Math.random() * 100) - })); - const score = listData.possibleVotes.map((pv, i) => { - return pv.score * votes[i].votes; - }).reduce((a, b) => a + b, 0); - return { - id: item.id, - list: item.list, - title: item.title, - description: item.description, - votes, - userVotes: loggedIn ? listData.possibleVotes.map(pv => ({ - name: pv.name, - votes: item[pv.name] || 0 - })) : undefined, - score - }; - }).sort((a, b) => b.score - a.score) - })); -} - -export function createItem(token, list, title, description) { - if(token !== "developer") - return Promise.reject(new Error("Not logged in as developer")); - let newItem = { - id: Math.random() + "", - list, - title, - description - }; - allItems[newItem.id] = newItem; - lists[list].items.push(newItem); - return delay(500).then(() => ({ - ...newItem, - votes: lists[list].possibleVotes.map(pv => ({ - name: pv.name, - votes: 0 - })), - userVotes: lists[list].possibleVotes.map(pv => ({ - name: pv.name, - votes: 0 - })), - score: 0 - })); -} - -export function vote(token, itemId, voteName, value) { - if(token !== "developer") - return Promise.reject(new Error("Not logged in as developer")); - var listId = allItems[itemId].list; - let listData = lists[listId]; - let pv = listData.possibleVotes.filter(pv => pv.name === voteName)[0]; - if(pv.currency) { - usedCurrencies[pv.currency] += value; - } - allItems[itemId][voteName] = (allItems[itemId][voteName] || 0) + value; - return delay(500).then(() => true); -} - -export function configItem(token, itemId, config) { - if(token !== "developer") - return Promise.reject(new Error("Not logged in as developer")); - var item = allItems[itemId]; - Object.keys(config).forEach(key => { - item[key] = config[key]; - }); - return delay(500).then(() => true); -} diff --git a/src/components/Vote/api.js b/src/components/Vote/api.js deleted file mode 100755 index e29c7077fb58..000000000000 --- a/src/components/Vote/api.js +++ /dev/null @@ -1,139 +0,0 @@ -import { - isLoginActive as devIsLoginActive, - startLogin as devStartLogin, - continueLogin as devContinueLogin, - getSelf as devGetSelf, - getList as devGetList, - createItem as devCreateItem, - vote as devVote, - configItem as devConfigItem -} from "./api.dev"; - -const API_URL = "https://oswils44oj.execute-api.us-east-1.amazonaws.com/production/"; -const GITHUB_CLIENT_ID = "4d355e2799cb8926c665"; -const PRODUCTION_HOST = "webpack.js.org"; - -// You can test the production mode with a host entry, -// or by setting PRODUCTION_HOST to "localhost:3000" and stealing localStorage.voteAppToken from the production side. - -function checkResult(result) { - if(!result) - throw new Error("No result received"); - if(result.errorMessage) - throw new Error(result.errorMessage); - return result; -} - -export function isLoginActive() { - if(window.location.host !== PRODUCTION_HOST) - return devIsLoginActive(); - return /^\?code=([^&]*)&state=([^&]*)/.test(window.location.search); -} - -export function startLogin(callbackUrl) { - if(window.location.host !== PRODUCTION_HOST) - return devStartLogin(callbackUrl); - let state = "" + Math.random(); - window.localStorage.githubState = state; - window.location = "https://github.com/login/oauth/authorize?client_id=" + GITHUB_CLIENT_ID + "&scope=user:email&state=" + state + "&allow_signup=false&redirect_uri=" + encodeURIComponent(callbackUrl); - return Promise.resolve(); -} - -export function continueLogin() { - if(window.location.host !== PRODUCTION_HOST) - return devContinueLogin(); - const match = /^\?code=([^&]*)&state=([^&]*)/.exec(window.location.search); - if(match) { - return login(match[1], match[2]).then(result => { - setTimeout(() => { - let href = window.location.href; - window.location = href.substr(0, href.length - window.location.search.length); - }, 100); - return result; - }); - } - return Promise.resolve(); -} - -function login(code, state) { - if(state !== window.localStorage.githubState) - return Promise.reject(new Error("Request state doesn't match (Login was triggered by 3rd party)")); - delete window.localStorage.githubState; - return fetch(API_URL + "/login", { - headers: { - "Content-Type": "application/json" - }, - method: "POST", - body: JSON.stringify({ - code, - state - }) - }).then((res) => res.json()).then(checkResult).then(result => { - if(!result.token) - throw new Error("No token received from API"); - return result.token; - }); -} - -export function getSelf(token) { - if(window.location.host !== PRODUCTION_HOST) - return devGetSelf(token); - return fetch(API_URL + "/self?token=" + token, { - mode: "cors" - }).then((res) => res.json()).then(checkResult); -} - -export function getList(token, name) { - if(window.location.host !== PRODUCTION_HOST) - return devGetList(token, name); - return fetch(API_URL + "/list/" + name + (token ? "?token=" + token : ""), { - mode: "cors" - }).then((res) => res.json()).then(checkResult); -} - -export function createItem(token, list, title, description) { - if(window.location.host !== PRODUCTION_HOST) - return devCreateItem(token, list, title, description); - return fetch(API_URL + "/list/" + list + "?token=" + token, { - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - title, - description - }), - method: "POST" - }).then((res) => res.json()).then(checkResult); -} - -export function vote(token, itemId, voteName, value) { - if(window.location.host !== PRODUCTION_HOST) - return devVote(token, itemId, voteName, value); - return fetch(API_URL + "/vote/" + itemId + "/" + voteName + "?token=" + token, { - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - count: value - }), - method: "POST" - }).then((res) => res.json()).then(checkResult).then(result => { - return true; - }); -} - -export function configItem(token, itemId, config) { - if(window.location.host !== PRODUCTION_HOST) - return devConfigItem(token, itemId, config); - return fetch(API_URL + "/config/" + itemId + "?token=" + token, { - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - config: config - }), - method: "POST" - }).then((res) => res.json()).then(checkResult).then(result => { - return true; - }); -} diff --git a/src/content/api/cli.md b/src/content/api/cli.md index 85f4c104f6ff..0c0bda477e18 100755 --- a/src/content/api/cli.md +++ b/src/content/api/cli.md @@ -4,6 +4,7 @@ sort: 2 contributors: - ev1stensberg - simon04 + - tbroadley related: - title: Analyzing Build Statistics url: https://survivejs.com/webpack/optimizing-build/analyzing-build-statistics/ @@ -215,7 +216,6 @@ webpack.js index=./src/index.js index2=./src/index2.js --output-path='./dist' -- 参数 | 说明 ------------------------- | ---------------------- `--watch`, `-w` | 观察文件系统的变化 -`--save`, `-s` | 在保存的时候重新编译,无论文件是否变化 `--watch-aggregate-timeout` | 指定一个毫秒数,在这个时间内,文件若发送了多次变化,会被合并 `--watch-poll` | 轮询观察文件变化的时间间隔(同时会打开轮询机制) `--watch-stdin`, `--stdin` | 当 stdin 关闭时,退出进程 diff --git a/src/content/api/hot-module-replacement.md b/src/content/api/hot-module-replacement.md index c9cc3f85d754..d214d6172483 100755 --- a/src/content/api/hot-module-replacement.md +++ b/src/content/api/hot-module-replacement.md @@ -3,6 +3,7 @@ title: 模块热替换(Hot Module Replacement) contributors: - sokra - skipjack + - tbroadley related: - title: 概念 - 模块热替换(Hot Module Replacement) url: /concepts/hot-module-replacement @@ -117,7 +118,7 @@ module.hot.apply(options).then(outdatedModules => { - `ignoreUnaccepted` (boolean): Ignore changes made to unaccepted modules. - `ignoreDeclined` (boolean): Ignore changes made to declined modules. -- `ignoreErrored` (boolean): Ignore errors throw in accept handlers, error handlers and while reevaulating module. +- `ignoreErrored` (boolean): Ignore errors throw in accept handlers, error handlers and while reevaluating module. - `onDeclined` (function(info)): Notifier for declined modules - `onUnaccepted` (function(info)): Notifier for unaccepted modules - `onAccepted` (function(info)): Notifier for accepted modules diff --git a/src/content/api/index.md b/src/content/api/index.md index df95f7f100ff..d159cab85d6b 100755 --- a/src/content/api/index.md +++ b/src/content/api/index.md @@ -1,6 +1,8 @@ --- title: 引导 sort: 1 +contributors: + - tbroadley --- 可以使用各种接口来定制化编译过程(compilation process)。一些特性会在几个接口之间重叠,例如,配置选项可能会从 CLI 标记(flag)中获取参数,而其他已存在特性,则只能通过某个接口去获取。以下高级信息可以帮助你起步。 diff --git a/src/content/api/loaders.md b/src/content/api/loaders.md index 77a7f95b68a3..7f042856e786 100755 --- a/src/content/api/loaders.md +++ b/src/content/api/loaders.md @@ -4,22 +4,23 @@ sort: 4 contributors: - TheLarkInn - jhnns + - tbroadley --- -所谓 loader 只是导出为一个函数的 JavaScript 模块。[loader runner](https://github.com/webpack/loader-runner) 会调用这个函数,然后把上一个 loader 产生的结果或者资源文件(resource file)传入进去。函数的 `this` 上下文将由 webpack 填充,并且 [loader runner](https://github.com/webpack/loader-runner) 带有一些有用方法,可以使 loader 改变为异步调用方式,或者获取 query 参数。 +所谓 loader 只是一个导出为函数的 JavaScript 模块。[loader runner](https://github.com/webpack/loader-runner) 会调用这个函数,然后把上一个 loader 产生的结果或者资源文件(resource file)传入进去。函数的 `this` 上下文将由 webpack 填充,并且 [loader runner](https://github.com/webpack/loader-runner) 具有一些有用方法,可以使 loader 改变为异步调用方式,或者获取 query 参数。 第一个 loader 的传入参数只有一个:资源文件(resource file)的内容。compiler 需要得到最后一个 loader 产生的处理结果。这个处理结果应该是 `String` 或者 `Buffer`(被转换为一个 string),代表了模块的 JavaScript 源码。另外还可以传递一个可选的 SourceMap 结果(格式为 JSON 对象)。 -如果是单个处理结果,可以在**同步模式**中直接返回。如果有多个处理结果,则需要调用 `this.callback()`。在**异步模式**中,必须调用 `this.async()`,来指示 [loader runner](https://github.com/webpack/loader-runner) 等待异步结果,它会返回 `this.callback()` 回调函数,随后 loader 必须返回 `undefined` 并且调用该回调函数。 +如果是单个处理结果,可以在**同步模式**中直接返回。如果有多个处理结果,则必须调用 `this.callback()`。在**异步模式**中,必须调用 `this.async()`,来指示 [loader runner](https://github.com/webpack/loader-runner) 等待异步结果,它会返回 `this.callback()` 回调函数,随后 loader 必须返回 `undefined` 并且调用该回调函数。 ## 示例 -The following sections provide some basic examples of the different types of loaders. Note that the `map` and `meta` parameters are optional, see [`this.callback`](/api/loaders#this-callback) below. +以下部分提供了不同类型的 loader 的一些基本示例。注意,`map` 和 `meta` 参数是可选的,查看下面的 [`this.callback`](/api/loaders#this-callback)。 ### 同步 loader -Either `return` or `this.callback` can be used to return the transformed `content` synchronously: +无论是 `return` 还是 `this.callback` 都可以同步地返回转换后的 `content` 内容: __sync-loader.js__ @@ -29,20 +30,20 @@ module.exports = function(content, map, meta) { }; ``` -The `this.callback` method is more flexible as it allows multiple arguments to be passed as opposed to just the `content`. +`this.callback` 方法则更灵活,因为它允许传递多个参数,而不仅仅是`content`。 __sync-loader-with-multiple-results.js__ ``` js module.exports = function(content, map, meta) { this.callback(null, someSyncOperation(content), sourceMaps, meta); - return; // 当调用callback()时总是返回 undefined + return; // 当调用 callback() 时总是返回 undefined }; ``` ### 异步 loader -For asynchronous loaders, [`this.async`](/api/loaders#this-async) is used to retrieve the `callback` function: +对于异步 loader,使用 [`this.async`](/api/loaders#this-async) 来获取 `callback` 函数: __async-loader.js__ @@ -68,7 +69,7 @@ module.exports = function(content, map, meta) { }; ``` -T> loader 最初被设计为可以在同步 loader pipeline(如 Node.js ,使用 [enhanced-require](https://github.com/webpack/enhanced-require)),与异步 pipeline(如 webpack )中运行。然而在 Node.js 这样的单线程环境下进行耗时长的同步计算不是个好主意,我们建议尽可能地使您的 loader 异步化。但如果计算量很小,同步 loader 也是可以的。 +T> loader 最初被设计为可以在同步 loader pipeline(如 Node.js ,使用 [enhanced-require](https://github.com/webpack/enhanced-require)),与异步 pipeline(如 webpack )中运行。然而在 Node.js 这样的单线程环境下进行耗时长的同步计算不是个好主意,我们建议尽可能地使你的 loader 异步化。但如果计算量很小,同步 loader 也是可以的。 ### "Raw" loader @@ -88,11 +89,33 @@ module.exports.raw = true; ``` -### Pitching loader +### 越过 loader(Pitching loader) -loader **总是**从右到左地被调用,但是在一些情况下,loader 不需要关心之前处理的结果或者资源(resource),而是只关心**元数据(metadata)**。在 loader 被调用前(从右到左),loader 中的 `pitch` 方法会**从左到右**依次被调用。 +loader __总是__从右到左地被调用。有些情况下,loader 只关心 request 后面的__元数据(metadata)__,并且忽略前一个 loader 的结果。在实际(从右到左)执行 loader 之前,会先__从左到右__调用 loader 上的 `pitch` 方法。对于以下 [`use`](/configuration/module#rule-use) 配置: -如果中间某个 loader 的 `pitch` 方法返回了一个值,那么剩下的 loader 都会被跳过,转而从当前 loader 开始向左调用 loader。`data`可以在 pitch 和普通的 loader 调用间传递。 +``` js +use: [ + 'a-loader', + 'b-loader', + 'c-loader' +] +``` + +将会发生这些步骤: + +``` diff +|- a-loader `pitch` + |- b-loader `pitch` + |- c-loader `pitch` + |- requested module is picked up as a dependency + |- c-loader normal execution + |- b-loader normal execution +|- a-loader normal execution +``` + +那么,为什么 loader 可以利用 "跳跃(pitching)" 阶段呢? + +首先,传递给 `pitch` 方法的 `data`,在执行阶段也会暴露在 `this.data` 之下,并且可以用于在循环时,捕获和共享前面的信息。 ``` js module.exports = function(content) { @@ -100,20 +123,41 @@ module.exports = function(content) { }; module.exports.pitch = function(remainingRequest, precedingRequest, data) { - if(someCondition()) { - // 直接返回 - return "module.exports = require(" + JSON.stringify("-!" + remainingRequest) + ");"; - } data.value = 42; }; ``` +其次,如果某个 loader 在 `pitch` 方法中给出一个结果,那么这个过程会回过身来,并跳过剩下的 loader。在我们上面的例子中,如果 `b-loader` 的 `pitch` 方法返回了一些东西: + +``` js +module.exports = function(content) { + return someSyncOperation(content); +}; + +module.exports.pitch = function(remainingRequest, precedingRequest, data) { + if (someCondition()) { + return "module.exports = require(" + JSON.stringify("-!" + remainingRequest) + ");"; + } +}; +``` + +上面的步骤将被缩短为: + +``` diff +|- a-loader `pitch` + |- b-loader `pitch` returns a module +|- a-loader normal execution +``` + +查看 [bundle-loader](https://github.com/webpack-contrib/bundle-loader),了解如何以更有意义的方式使用此过程。 + ## loader 上下文 -loader context 表示在 loader 内使用 `this` 可以访问的一些方法或属性 +loader context 表示在 loader 内使用 `this` 可以访问的一些方法或属性。 -假设我们在 `/abc/file.js` 中这样请求加载别的模块: +假设我们这样请求加载别的模块: +在 `/abc/file.js` 中: ``` js require("./loader1?xyz!loader2!./resource?rrr"); @@ -127,14 +171,14 @@ require("./loader1?xyz!loader2!./resource?rrr"); ### `this.context` -**模块所在的目录。**某些场景下这可能会有用处。 +**模块所在的目录。**可以用作解析其他模块路径的上下文。 在我们的例子中:这个属性为 `/abc`,因为 `resource.js` 在这个目录中 ### `this.request` -被解析出来的请求字符串。 +被解析出来的 request 字符串。 在我们的例子中:`"/abc/loader1.js?xyz!/abc/node_modules/loader2/index.js!/abc/resource.js?rrr"` @@ -163,21 +207,21 @@ this.callback( 1. 第一个参数必须是 `Error` 或者 `null` 2. 第二个参数是一个 `string` 或者 [`Buffer`](https://nodejs.org/api/buffer.html)。 3. 可选的:第三个参数必须是一个可以被[这个模块](https://github.com/mozilla/source-map)解析的 source map。 -4. 可选的:The fourth option, ignored by webpack, can be anything (e.g. some meta data). +4. 可选的:第四个选项,会被 webpack 忽略,可以是任何东西(例如一些元数据)。 -T> It can be useful to pass an abstract syntax tree (AST), like [`ESTree`](https://github.com/estree/estree), as the fourth argument (`meta`) to speed up the build time if you want to share common ASTs between loaders. +T> 可以将抽象语法树(abstract syntax tree - AST)(例如 [`ESTree`](https://github.com/estree/estree))作为第四个参数(`meta`),如果你想在多个 loader 之间共享通用的 AST,这样做有助于加速编译时间。 如果这个函数被调用的话,你应该返回 undefined 从而避免含糊的 loader 结果。 ### `this.async` -告诉 [loader-runner](https://github.com/webpack/loader-runner) 这个loader将会异步地回调。返回`this.callback`。 +告诉 [loader-runner](https://github.com/webpack/loader-runner) 这个 loader 将会异步地回调。返回 `this.callback`。 ### `this.data` -在 pitch 阶段和正常阶段之间共享的数据对象。 +在 pitch 阶段和正常阶段之间共享的 data 对象。 ### `this.cacheable` @@ -225,12 +269,12 @@ loaders = [{request: string, path: string, query: string, module: function}] 当前 loader 在 loader 数组中的索引。 -在我们的示例中:loader1:`0`,loader2:`1` +在我们的示例中:loader1 中得到:`0`,loader2 中得到:`1` ### `this.resource` -请求的资源部分,包括 query 参数。 +request 中的资源部分,包括 query 参数。 在我们的示例中:`"/abc/resource.js?rrr"` @@ -258,14 +302,14 @@ loaders = [{request: string, path: string, query: string, module: function}] ### `this.webpack` -如果是 Webpack 编译的,这个布尔值会被设置为真。 +如果是由 webpack 编译的,这个布尔值会被设置为真。 -T> loader 最初被设计为可以同时当 Babel transform 用。如果你编写了一个 loader 可以同时兼容二者,那么可以使用这个属性了解是否存在可用的 loaderContext 和 Webpack 的特性。 +T> loader 最初被设计为可以同时当 Babel transform 用。如果你编写了一个 loader 可以同时兼容二者,那么可以使用这个属性了解是否存在可用的 loaderContext 和 webpack 特性。 ### `this.sourceMap` -应该生成一个source map。因为生成 source map 可能会非常耗时,你应该确认 source map 确实被请求了。 +应该生成一个 source map。因为生成 source map 可能会非常耗时,你应该确认 source map 确实有必要请求。 ### `this.emitWarning` @@ -292,7 +336,7 @@ emitError(error: Error) loadModule(request: string, callback: function(err, source, sourceMap, module)) ``` -解析给定的 request 到一个模块,应用所有配置的 loader ,并且利用生成的 source 、sourceMap 和 模块实例(通常是 [`NormalModule`](https://github.com/webpack/webpack/blob/master/lib/NormalModule.js) 的一个实例),来进行回调。如果你需要知道其他模块的源代码来生成结果的话,你可以使用这个函数。 +解析给定的 request 到一个模块,应用所有配置的 loader ,并且在回调函数中传入生成的 source 、sourceMap 和 模块实例(通常是 [`NormalModule`](https://github.com/webpack/webpack/blob/master/lib/NormalModule.js) 的一个实例)。如果你需要获取其他模块的源代码来生成结果的话,你可以使用这个函数。 ### `this.resolve` @@ -311,7 +355,7 @@ addDependency(file: string) dependency(file: string) // 简写 ``` -加入一个文件作为产生 loader 结果的依赖,使它们的任何变化可以被监听到。例如,[html-loader](https://github.com/webpack/html-loader) 就使用了这个技巧。当它发现 `src` 和 `src-set` 属性时,就会把这些属性上的 url 加入到被解析的 html 文件的依赖中。 +加入一个文件作为产生 loader 结果的依赖,使它们的任何变化可以被监听到。例如,[`html-loader`](https://github.com/webpack/html-loader) 就使用了这个技巧,当它发现 `src` 和 `src-set` 属性时,就会把这些属性上的 url 加入到被解析的 html 文件的依赖中。 ### `this.addContextDependency` @@ -320,7 +364,7 @@ dependency(file: string) // 简写 addContextDependency(directory: string) ``` -把文件夹作为 loader 的依赖加入。 +把文件夹作为 loader 结果的依赖加入。 ### `this.clearDependencies` @@ -329,7 +373,7 @@ addContextDependency(directory: string) clearDependencies() ``` -移除 loader 所有的依赖。甚至自己和其它 loader 的初始依赖。考虑使用 `pitch`。 +移除 loader 结果的所有依赖。甚至自己和其它 loader 的初始依赖。考虑使用 `pitch`。 ### `this.emitFile` @@ -338,7 +382,7 @@ clearDependencies() emitFile(name: string, content: Buffer|string, sourceMap: {...}) ``` -产生一个文件。这是 webpack 独有的(原文:This is webpack-specific)。 +产生一个文件。这是 webpack 特有的。 ### `this.fs` @@ -346,7 +390,7 @@ emitFile(name: string, content: Buffer|string, sourceMap: {...}) 用于访问 `compilation` 的 `inputFileSystem` 属性。 -## Deprecated context properties +## 废弃的上下文属性 W> 强烈建议不要使用这些属性,因为我们打算移除它们。它们仍然列在此处用于文档目的。 @@ -357,7 +401,7 @@ W> 强烈建议不要使用这些属性,因为我们打算移除它们。它 exec(code: string, filename: string) ``` -以模块的方式执行一些代码片段。See [this comment](https://github.com/webpack/webpack.js.org/issues/1268#issuecomment-313513988) for a replacement method if needed. +以模块的方式执行一些代码片段。如果需要,请查看[这里的评论](https://github.com/webpack/webpack.js.org/issues/1268#issuecomment-313513988)以获取替换方法。 ### `this.resolveSync` @@ -366,7 +410,7 @@ exec(code: string, filename: string) resolveSync(context: string, request: string) -> string ``` -像 require 表达式一样解析一个 request 。(同步) +像 require 表达式一样解析一个 request。 ### `this.value` @@ -376,7 +420,7 @@ resolveSync(context: string, request: string) -> string ### `this.inputValue` -从上一个 loader 那里传递过来的值。如果你会以模块的方式处理输入参数,建议预先读入这个变量。(为了性能因素) +从上一个 loader 那里传递过来的值。如果你会以模块的方式处理输入参数,建议预先读入这个变量(为了性能因素)。 ### `this.options` @@ -386,7 +430,7 @@ options 的值将会传递给 Complier ### `this.debug` -一个布尔值,当处于 debug 模式时为真。 +一个布尔值,当处于 debug 模式时为 true。 ### `this.minimize` @@ -396,12 +440,12 @@ options 的值将会传递给 Complier ### `this._compilation` -一种 hack 写法。用于访问 Webpack 的 Compilation 对象。 +一种 hack 写法。用于访问 webpack 的 Compilation 对象。 ### `this._compiler` -一种 hack 写法。用于访问 Webpack 的 Compiler 对象。 +一种 hack 写法。用于访问 webpack 的 Compiler 对象。 ### `this._module` diff --git a/src/content/api/module-variables.md b/src/content/api/module-variables.md index 4c59b5918cc0..2133e043acc1 100755 --- a/src/content/api/module-variables.md +++ b/src/content/api/module-variables.md @@ -6,6 +6,7 @@ contributors: - skipjack - sokra - ahmehri + - tbroadley related: - title: CommonJS url: https://en.wikipedia.org/wiki/CommonJS diff --git a/src/content/concepts/index.md b/src/content/concepts/index.md index 7f8c9973c909..deaa8cb1a0c3 100755 --- a/src/content/concepts/index.md +++ b/src/content/concepts/index.md @@ -74,12 +74,14 @@ T> `output` 属性还有[更多可配置的特性](/configuration/output),如 本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图可以直接引用的模块。 +W> 注意,loader 能够 `import` 导入任何类型的模块(例如 `.css` 文件),这是 webpack 特有的功能,其他打包程序或任务执行器的可能并不支持。我们认为这种语言扩展是有很必要的,因为这可以使开发人员创建出更准确的依赖关系图。 + 在更高层面,在 webpack 的配置中 **loader** 有两个目标。 1. 识别出应该被对应的 loader 进行转换的那些文件。(使用 `test` 属性) 2. 转换这些文件,从而使其能够被添加到依赖图中(并且最终添加到 bundle 中)(`use` 属性) -**webpack.config.js** +__webpack.config.js__ ```javascript const path = require('path'); diff --git a/src/content/concepts/loaders.md b/src/content/concepts/loaders.md index 0e222a46dd4c..c9a245e7d31e 100755 --- a/src/content/concepts/loaders.md +++ b/src/content/concepts/loaders.md @@ -101,7 +101,7 @@ webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader' ## Loader 特性 -* loader 支持链式传递。能够对资源使用流水线(pipeline)。一组链式的 loader 将按照先后顺序进行编译。loader 链中的第一个 loader 返回值给下一个 loader。在最后一个 loader,返回 webpack 所预期的 JavaScript。 +* loader 支持链式传递。能够对资源使用流水线(pipeline)。一组链式的 loader 将按照相反的顺序执行。loader 链中的第一个 loader 返回值给下一个 loader。在最后一个 loader,返回 webpack 所预期的 JavaScript。 * loader 可以是同步的,也可以是异步的。 * loader 运行在 Node.js 中,并且能够执行任何可能的操作。 * loader 接收查询参数。用于对 loader 传递配置。 @@ -110,7 +110,8 @@ webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader' * 插件(plugin)可以为 loader 带来更多特性。 * loader 能够产生额外的任意文件。 -loader 通过(loader)预处理函数,为 JavaScript 生态系统提供了更多能力。用户现在可以更加灵活地引入细粒度逻辑,例如压缩、打包、语言翻译和[其他更多](/loaders)。 +loader 通过(loader)预处理函数,为 JavaScript 生态系统提供了更多能力。 +用户现在可以更加灵活地引入细粒度逻辑,例如压缩、打包、语言翻译和[其他更多](/loaders)。 ## 解析 Loader diff --git a/src/content/configuration/configuration-languages.md b/src/content/configuration/configuration-languages.md index 1775c9ea3dd3..009f18edde36 100755 --- a/src/content/configuration/configuration-languages.md +++ b/src/content/configuration/configuration-languages.md @@ -7,6 +7,7 @@ contributors: - tarang9211 - simon04 - peterblazejewicz + - youta1119 --- webpack 接受以多种编程和数据语言编写的配置文件。支持的文件扩展名列表,可以在 [node-interpret](https://github.com/js-cli/js-interpret) 包中找到。使用 [node-interpret](https://github.com/js-cli/js-interpret),webpack 可以处理许多不同类型的配置文件。 @@ -39,6 +40,48 @@ const config: webpack.Configuration = { export default config; ``` +Not that you'll also need to check your `tsconfig.json` file. If the module in `compilerOptions` in `tsconfig.json` is `commonjs`, the setting is complete, else webpack will fail with an error. This occurs because `ts-node` does not support any module syntax other than `commonjs`. + +There are two solutions to this issue: + +- Modify `tsconfig.json`. +- Install `tsconfig-paths`. + +The __first option__ is to open your `tsconfig.json` file and look for `compilerOptions`. Set `target` to `"ES5"` and `module` to `"CommonJS"` (or completely remove the `module` option). + +The __second option__ is to install the `tsconfig-paths` package: + +``` bash +npm install --save-dev tsconfig-paths +``` + +And create a separate TypeScript configuration specifically for your webpack configs: + +__tsconfig-for-webpack-config.json__ + +``` json +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5" + } +} +``` + +T> `ts-node` can resolve a `tsconfig.json` file using the environment variable provided by `tsconfig-path`. + +Then set the environment variable `process.env.TS_NODE_PROJECT` provided by `tsconfig-path` like so: + +__package.json__ + +```json +{ + "scripts": { + "build": "TS_NODE_PROJECT=\"tsconfig-for-webpack-config.json\" webpack" + } +} +``` + ## CoffeeScript diff --git a/src/content/configuration/devtool.md b/src/content/configuration/devtool.md index 59e37b1c74ba..f7d6bfb3982e 100755 --- a/src/content/configuration/devtool.md +++ b/src/content/configuration/devtool.md @@ -30,7 +30,7 @@ T> Instead of using the `devtool` option you can also use `SourceMapDevToolPlugi devtool | build | rebuild | production | quality ------------------------------ | ----- | ------- | ---------- | ----------------------------- -(none) | +++ | +++ | no | bundled code +(none) | +++ | +++ | yes | bundled code eval | +++ | +++ | no | generated code cheap-eval-source-map | + | ++ | no | transformed code (lines only) cheap-module-eval-source-map | o | ++ | no | original source (lines only) diff --git a/src/content/configuration/externals.md b/src/content/configuration/externals.md index 6c95b0f003cb..ddbc26b01483 100755 --- a/src/content/configuration/externals.md +++ b/src/content/configuration/externals.md @@ -9,7 +9,7 @@ contributors: `externals` 配置选项提供了「从输出的 bundle 中排除依赖」的方法。相反,所创建的 bundle 依赖于那些存在于用户环境(consumer's environment)中的依赖。此功能通常对 __library 开发人员__来说是最有用的,然而也会有各种各样的应用程序用到它。 -T> __用户(consumer)__,在这里是指,引用了「使用 webpack 打包的 library」 的任何终端用户的应用程序(end user application)。 +T> __用户(consumer)__,在这里是指,引用了「使用 webpack 打包的 library」的所有终端用户的应用程序(end user application)。 ## `externals` @@ -20,19 +20,19 @@ __防止__将某些 `import` 的包(package)__打包__到 bundle 中,而是在 例如,从 CDN 引入 [jQuery](https://jquery.com/),而不是把它打包: -**index.html** +__index.html__ -```html -... - -... + crossorigin="anonymous"> + ``` -**webpack.config.js** +__webpack.config.js__ -```javascript +``` js externals: { jquery: 'jQuery' } @@ -40,7 +40,7 @@ externals: { 这样就剥离了那些不需要改动的依赖模块,换句话,下面展示的代码还可以正常运行: -```javascript +``` js import $ from 'jquery'; $('.my-element').animate(...); @@ -48,22 +48,22 @@ $('.my-element').animate(...); 具有外部依赖(external dependency)的 bundle 可以在各种模块上下文(module context)中使用,例如 [CommonJS, AMD, 全局变量和 ES2015 模块](/concepts/modules)。外部 library 可能是以下任何一种形式: -* __root__ - 外部 library 能够作为全局变量使用。用户可以通过在 script 标签中引入来实现。这是 externals 的默认设置。 -* __commonjs__ - 用户(consumer)应用程序可能使用 CommonJS 模块系统,因此外部 library 应该使用 CommonJS 模块系统,并且应该是一个 CommonJS 模块。 -* __commonjs2__ - 类似上面几行,但导出的是 `module.exports.default`。 -* __amd__ - 类似上面几行,但使用 AMD 模块系统。 +- __root__:可以通过一个全局变量访问 library(例如,通过 script 标签)。 +- __commonjs__:可以将 library 作为一个 CommonJS 模块访问。 +- __commonjs2__:和上面的类似,但导出的是 `module.exports.default`. +- __amd__:类似于 `commonjs`,但使用 AMD 模块系统。 -`externals` 接受各种语法,并且按照不同方式去解释他们。 +可以接受各种语法…… ### string -externals 中的 `jQuery`,表示你的 bundle 需要访问全局形式的 `jQuery` 变量。 +请查看上面的例子。属性名称是 `jquery`,表示应该排除 `import $ from 'jquery'` 中的 `jquery` 模块。为了替换这个模块,`jQuery` 的值将被用来检索一个全局的 `jQuery` 变量。换句话说,当设置为一个字符串时,它将被视为`全局的`(定义在上面和下面)。 ### array -```javascript +``` js externals: { subtract: ['./math', 'subtract'] } @@ -74,7 +74,7 @@ externals: { ### object -```javascript +``` js externals : { react: 'react' } @@ -85,11 +85,11 @@ externals : { lodash : { commonjs: "lodash", amd: "lodash", - root: "_" // indicates global variable + root: "_" // 指向全局变量 } } -// or +// 或者 externals : { subtract : { @@ -98,16 +98,16 @@ externals : { } ``` -此语法用于描述所有外部 library 可用的访问方式。这里 `lodash` 这个外部 library 可以在 AMD 和 CommonJS 模块系统中通过 `lodash` 访问,但在全局变量形式下用 `_` 访问。`subtract` 可以通过全局 `math` 对象下的属性 `subtract` 访问(例如 `window['math']['subtract']`)。 +此语法用于描述外部 library 所有可用的访问方式。这里 `lodash` 这个外部 library 可以在 AMD 和 CommonJS 模块系统中通过 `lodash` 访问,但在全局变量形式下用 `_` 访问。`subtract` 可以通过全局 `math` 对象下的属性 `subtract` 访问(例如 `window['math']['subtract']`)。 ### function -It might be useful to define your own function to control the behavior of what you want to externalize from webpack. [webpack-node-externals](https://www.npmjs.com/package/webpack-node-externals), for example, excludes all modules from the node_modules and provides some options to, for example, whitelist packages. +It might be useful to define your own function to control the behavior of what you want to externalize from webpack. [webpack-node-externals](https://www.npmjs.com/package/webpack-node-externals), for example, excludes all modules from the `node_modules` directory and provides some options to, for example, whitelist packages. It basically comes down to this: -```javascript +``` js externals: [ function(context, request, callback) { if (/^yourregex$/.test(request)){ @@ -125,7 +125,7 @@ The `'commonjs ' + request` defines the type of module that needs to be external Every dependency that matches the given regular expression will be excluded from the output bundles. -```javascript +``` js externals: /^(jquery|\$)$/i ``` diff --git a/src/content/configuration/output.md b/src/content/configuration/output.md index 9fe25706cfa0..d5757b912deb 100755 --- a/src/content/configuration/output.md +++ b/src/content/configuration/output.md @@ -295,7 +295,7 @@ JSONP 函数用于异步加载(async load) chunk,或者拼接多个初始 chun `output.library` 的值的作用,取决于[`output.libraryTarget`](#output-librarytarget) 选项的值;完整的详细信息请查阅该章节。注意,`output.libraryTarget` 的默认选项是 `var`,所以如果使用以下配置选项: -```javascript +``` js output: { library: "MyLibrary" } @@ -303,6 +303,8 @@ output: { 如果生成的输出文件,是在 HTML 页面中作为一个 script 标签引入,则变量 `MyLibrary` 将与入口文件的返回值绑定。 +W> 注意,如果将`数组`作为 `entry`,那么只会暴露数组中的最后一个模块。如果将`对象`作为 `entry`,还可以使用`数组`语法暴露(具体查看[这个示例](https://github.com/webpack/webpack/tree/master/examples/multi-part-library) for details))。 + T> 有关 `output.library` 以及 `output.libraryTarget` 详细信息,请查看[创建 library 指南](/guides/author-libraries)。 @@ -312,41 +314,38 @@ T> 有关 `output.library` 以及 `output.libraryTarget` 详细信息,请查 > Default: `_entry_return_` -Configure which module or modules will be exposed via the `libraryTarget`. - -The default value `_entry_return_` is the namespace or default module returned by your entry file. - -The examples below demonstrate the effect of this config when using `libraryTarget: "var"`, but any target may be used. +Configure which module or modules will be exposed via the `libraryTarget`. The default `_entry_return_` value is the namespace or default module returned by your entry file. The examples below demonstrate the effect of this config when using `libraryTarget: "var"`, but any target may be used. The following configurations are supported: `libraryExport: "default"` - The **default export of your entry point** will be assigned to the library target: -```javascript +``` js // if your entry has a default export of `MyDefaultModule` var MyDefaultModule = _entry_return_.default; ``` `libraryExport: "MyModule"` - The **specified module** will be assigned to the library target: -```javascript +``` js var MyModule = _entry_return_.MyModule; ``` `libraryExport: ["MyModule", "MySubModule"]` - The array is interpreted as a **path to a module** to be assigned to the library target: -```javascript +``` js var MySubModule = _entry_return_.MyModule.MySubModule; ``` -如同以上示例中所展示,入口起点的返回值,与这些具名变量绑定在一起,因此,生成的 library 的用法如下: +With the `libraryExport` configurations specified above, the resulting libraries could be utilized as such: -```javascript +``` js MyDefaultModule.doSomething(); MyModule.doSomething(); MySubModule.doSomething(); ``` + ## `output.libraryTarget` `string` @@ -363,7 +362,7 @@ T> 注意,下面的示例代码中的 `_entry_return_` 是入口起点返回 `libraryTarget: "var"` - (默认值)当 library 加载完成,**入口起点的返回值**将分配给一个变量: -```javascript +``` js var MyLibrary = _entry_return_; // 在一个单独的 script…… @@ -375,7 +374,7 @@ W> 当使用此选项时,将 `output.library` 设置为空,会因为没有 `libraryTarget: "assign"` - 这将产生一个隐含的全局变量,可能会潜在地重新分配到全局中已存在的值(谨慎使用)。. -``` javascript +``` js MyLibrary = _entry_return_; ``` @@ -390,7 +389,7 @@ W> 当使用此选项时,将 `output.library` 设置为空,将产生一个 如果 `output.library` 未赋值为一个非空字符串,则默认行为是,将入口起点返回的所有属性都赋值给一个对象(此对象由 `output.libraryTarget` 特定),通过如下代码片段: -```javascript +``` js (function(e, a) { for(var i in a) e[i] = a[i]; }(${output.libraryTarget}, _entry_return_) ``` @@ -398,7 +397,7 @@ W> 注意,不设置 `output.library` 将导致由入口起点返回的所有 `libraryTarget: "this"` - **入口起点的返回值**将分配给 this 的一个属性(此名称由 `output.library` 定义)下,`this` 的含义取决于你: -```javascript +``` js this["MyLibrary"] = _entry_return_; // 在一个单独的 script…… @@ -408,7 +407,7 @@ MyLibrary.doSomething(); // 如果 this 是 window `libraryTarget: "window"` - **入口起点的返回值**将使用 `output.library` 中定义的值,分配给 `window` 对象的这个属性下。 -```javascript +``` js window["MyLibrary"] = _entry_return_; window.MyLibrary.doSomething(); @@ -417,7 +416,7 @@ window.MyLibrary.doSomething(); `libraryTarget: "global"` - **入口起点的返回值**将使用 `output.library` 中定义的值,分配给 `global` 对象的这个属性下。 -```javascript +``` js global["MyLibrary"] = _entry_return_; global.MyLibrary.doSomething(); @@ -426,7 +425,7 @@ global.MyLibrary.doSomething(); `libraryTarget: "commonjs"` - **入口起点的返回值**将使用 `output.library` 中定义的值,分配给 exports 对象。这个名称也意味着,模块用于 CommonJS 环境: -```javascript +``` js exports["MyLibrary"] = _entry_return_; require("MyLibrary").doSomething(); @@ -439,7 +438,7 @@ require("MyLibrary").doSomething(); `libraryTarget: "commonjs2"` - **入口起点的返回值**将分配给 `module.exports` 对象。这个名称也意味着模块用于 CommonJS 环境: -```javascript +``` js module.exports = _entry_return_; require("MyLibrary").doSomething(); @@ -456,7 +455,7 @@ AMD 模块要求入口 chunk(例如使用 `+